From 424784ee45f9f9d6739303f64abc08c5ea3e1905 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 12 Feb 2020 16:03:37 +0200 Subject: [PATCH 01/12] Added before/after request handlers that add the execution time --- monkey/monkey_island/cc/app.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py index be2430dda..4c5c64ec8 100644 --- a/monkey/monkey_island/cc/app.py +++ b/monkey/monkey_island/cc/app.py @@ -1,8 +1,11 @@ +import json +import logging import os +import time import uuid import flask_restful -from flask import Flask, send_from_directory, Response +from flask import Flask, send_from_directory, Response, g from werkzeug.exceptions import NotFound from monkey_island.cc.auth import init_jwt @@ -41,6 +44,8 @@ __author__ = 'Barak' HOME_FILE = 'index.html' +logger = logging.getLogger(__name__) + def serve_static_file(static_path): if static_path.startswith('api/'): @@ -119,6 +124,25 @@ def init_api_resources(api): api.add_resource(LogTest, '/api/test/log') +def init_app_execution_time_calc(app): + @app.before_request + def before_request(): + g.start = time.time() + logger.debug("inb4 next request?") + + @app.after_request + def after_request(response): + diff_in_ms = int((time.time() - g.start)*1000) + logger.debug(f"After request") + if response.response: + if response.content_type == "application/json": + response_data = json.loads(response.get_data()) + if isinstance(response_data, dict): + response_data["execution_time_ms"] = diff_in_ms + response.set_data(json.dumps(response_data)) + return response + + def init_app(mongo_url): app = Flask(__name__) @@ -128,6 +152,7 @@ def init_app(mongo_url): init_app_config(app, mongo_url) init_app_services(app) init_app_url_rules(app) + init_app_execution_time_calc(app) init_api_resources(api) return app From 51099504e36b31e904c6356946ff51d37125fbef Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 12 Feb 2020 16:05:30 +0200 Subject: [PATCH 02/12] Revert "Added before/after request handlers that add the execution time" This reverts commit 424784ee45f9f9d6739303f64abc08c5ea3e1905. --- monkey/monkey_island/cc/app.py | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py index 4c5c64ec8..be2430dda 100644 --- a/monkey/monkey_island/cc/app.py +++ b/monkey/monkey_island/cc/app.py @@ -1,11 +1,8 @@ -import json -import logging import os -import time import uuid import flask_restful -from flask import Flask, send_from_directory, Response, g +from flask import Flask, send_from_directory, Response from werkzeug.exceptions import NotFound from monkey_island.cc.auth import init_jwt @@ -44,8 +41,6 @@ __author__ = 'Barak' HOME_FILE = 'index.html' -logger = logging.getLogger(__name__) - def serve_static_file(static_path): if static_path.startswith('api/'): @@ -124,25 +119,6 @@ def init_api_resources(api): api.add_resource(LogTest, '/api/test/log') -def init_app_execution_time_calc(app): - @app.before_request - def before_request(): - g.start = time.time() - logger.debug("inb4 next request?") - - @app.after_request - def after_request(response): - diff_in_ms = int((time.time() - g.start)*1000) - logger.debug(f"After request") - if response.response: - if response.content_type == "application/json": - response_data = json.loads(response.get_data()) - if isinstance(response_data, dict): - response_data["execution_time_ms"] = diff_in_ms - response.set_data(json.dumps(response_data)) - return response - - def init_app(mongo_url): app = Flask(__name__) @@ -152,7 +128,6 @@ def init_app(mongo_url): init_app_config(app, mongo_url) init_app_services(app) init_app_url_rules(app) - init_app_execution_time_calc(app) init_api_resources(api) return app From 4461097c601bdeaa090f8552e32f734ab9a85da1 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 23 Feb 2020 14:02:18 +0200 Subject: [PATCH 03/12] Added the API /test/clear_caches Currently clears only the reports from Mongo --- .../island_client/monkey_island_client.py | 10 ++++++ .../island_client/monkey_island_requests.py | 8 +++++ monkey/monkey_island/cc/app.py | 2 ++ .../cc/resources/test/__init__.py | 2 +- .../cc/resources/test/clear_caches.py | 35 +++++++++++++++++++ .../cc/services/attack/attack_report.py | 8 +++++ .../cc/services/reporting/report.py | 13 +++++++ 7 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 monkey/monkey_island/cc/resources/test/clear_caches.py diff --git a/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py b/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py index 479c41bab..53f102a72 100644 --- a/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py +++ b/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py @@ -85,3 +85,13 @@ class MonkeyIslandClient(object): def is_all_monkeys_dead(self): query = {'dead': False} return len(self.find_monkeys_in_db(query)) == 0 + + def clear_caches(self): + """ + Tries to clear caches. + :raises: If error (by error code), raises the error + :return: The response + """ + response = self.requests.delete("api/test/clear_caches") + response.raise_for_status() + return response diff --git a/envs/monkey_zoo/blackbox/island_client/monkey_island_requests.py b/envs/monkey_zoo/blackbox/island_client/monkey_island_requests.py index 388115463..23f259a9c 100644 --- a/envs/monkey_zoo/blackbox/island_client/monkey_island_requests.py +++ b/envs/monkey_zoo/blackbox/island_client/monkey_island_requests.py @@ -61,6 +61,14 @@ class MonkeyIslandRequests(object): headers=self.get_jwt_header(), verify=False) + @_Decorators.refresh_jwt_token + def delete(self, url): + return requests.delete( # noqa: DOU123 + self.addr + url, + headers=self.get_jwt_header(), + verify=False + ) + @_Decorators.refresh_jwt_token def get_jwt_header(self): return {"Authorization": "JWT " + self.token} diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py index be2430dda..2af0e2230 100644 --- a/monkey/monkey_island/cc/app.py +++ b/monkey/monkey_island/cc/app.py @@ -25,6 +25,7 @@ from monkey_island.cc.resources.root import Root from monkey_island.cc.resources.telemetry import Telemetry from monkey_island.cc.resources.telemetry_feed import TelemetryFeed from monkey_island.cc.resources.pba_file_download import PBAFileDownload +from monkey_island.cc.resources.test.clear_caches import ClearCaches from monkey_island.cc.resources.version_update import VersionUpdate from monkey_island.cc.resources.pba_file_upload import FileUpload from monkey_island.cc.resources.attack.attack_config import AttackConfiguration @@ -116,6 +117,7 @@ def init_api_resources(api): api.add_resource(VersionUpdate, '/api/version-update', '/api/version-update/') api.add_resource(MonkeyTest, '/api/test/monkey') + api.add_resource(ClearCaches, '/api/test/clear_caches') api.add_resource(LogTest, '/api/test/log') diff --git a/monkey/monkey_island/cc/resources/test/__init__.py b/monkey/monkey_island/cc/resources/test/__init__.py index 28550f830..aa3c993b0 100644 --- a/monkey/monkey_island/cc/resources/test/__init__.py +++ b/monkey/monkey_island/cc/resources/test/__init__.py @@ -1,4 +1,4 @@ """ This package contains resources used by blackbox tests -to analize test results, download logs and so on. +to analyze test results, download logs and so on. """ diff --git a/monkey/monkey_island/cc/resources/test/clear_caches.py b/monkey/monkey_island/cc/resources/test/clear_caches.py new file mode 100644 index 000000000..e23629a18 --- /dev/null +++ b/monkey/monkey_island/cc/resources/test/clear_caches.py @@ -0,0 +1,35 @@ +import logging + +import flask_restful + +from monkey_island.cc.auth import jwt_required +from monkey_island.cc.services.attack.attack_report import AttackReportService +from monkey_island.cc.services.reporting.report import ReportService + +NOT_ALL_REPORTS_DELETED = "Not all reports have been cleared from the DB!" + +logger = logging.getLogger(__name__) + + +class ClearCaches(flask_restful.Resource): + """ + Used for timing tests - we want to get actual execution time of functions in BlackBox without caching - so we use this + to clear the caches. + :note: DO NOT CALL THIS IN PRODUCTION CODE as this will slow down the user experience. + """ + @jwt_required() + def delete(self, **kw): + try: + logger.warning("Trying to clear caches! Make sure this is not production") + ReportService.delete_saved_report_if_exists() + AttackReportService.delete_saved_report_if_exists() + # TODO: Monkey.clear_caches(), clear LRU caches of function in the Monkey object + except RuntimeError as e: + logger.exception(e) + flask_restful.abort(500, error_info=str(e)) + + if ReportService.is_report_generated() or AttackReportService.is_report_generated(): + logger.exception(NOT_ALL_REPORTS_DELETED) + flask_restful.abort(500, error_info=NOT_ALL_REPORTS_DELETED) + + return {"success": "true"} diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py index 43cca0876..2f1e9dcf8 100644 --- a/monkey/monkey_island/cc/services/attack/attack_report.py +++ b/monkey/monkey_island/cc/services/attack/attack_report.py @@ -103,3 +103,11 @@ class AttackReportService: """ generated_report = mongo.db.attack_report.find_one({}) return generated_report is not None + + @staticmethod + def delete_saved_report_if_exists(): + if AttackReportService.is_report_generated(): + latest_report = mongo.db.attack_report.find_one({'name': REPORT_NAME}) + delete_result = mongo.db.report.delete_one({"_id": latest_report['_id']}) + if delete_result.deleted_count != 1: + raise RuntimeError("Error while deleting report:" + str(delete_result)) diff --git a/monkey/monkey_island/cc/services/reporting/report.py b/monkey/monkey_island/cc/services/reporting/report.py index 97e8fa4f1..f96279dd6 100644 --- a/monkey/monkey_island/cc/services/reporting/report.py +++ b/monkey/monkey_island/cc/services/reporting/report.py @@ -773,6 +773,19 @@ class ReportService: return False + @staticmethod + def delete_saved_report_if_exists(): + """ + This function clears the saved report from the DB. + :raises RuntimeError if deletion failed + """ + latest_report_doc = mongo.db.report.find_one({}, {'meta.latest_monkey_modifytime': 1}) + + if latest_report_doc: + delete_result = mongo.db.report.delete_one({"_id": latest_report_doc['_id']}) + if delete_result.deleted_count != 1: + raise RuntimeError("Error while deleting report:" + str(delete_result)) + @staticmethod def decode_dot_char_before_mongo_insert(report_dict): """ From 509dd09c843b06335c6d18c0c90b1bf3d22f8afb Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 23 Feb 2020 14:02:45 +0200 Subject: [PATCH 04/12] Changed log type --- monkey/monkey_island/cc/resources/test/clear_caches.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/resources/test/clear_caches.py b/monkey/monkey_island/cc/resources/test/clear_caches.py index e23629a18..1d12fb32e 100644 --- a/monkey/monkey_island/cc/resources/test/clear_caches.py +++ b/monkey/monkey_island/cc/resources/test/clear_caches.py @@ -29,7 +29,7 @@ class ClearCaches(flask_restful.Resource): flask_restful.abort(500, error_info=str(e)) if ReportService.is_report_generated() or AttackReportService.is_report_generated(): - logger.exception(NOT_ALL_REPORTS_DELETED) + logger.error(NOT_ALL_REPORTS_DELETED) flask_restful.abort(500, error_info=NOT_ALL_REPORTS_DELETED) return {"success": "true"} From 20be94d6064bbb5d33920152b08c3503f52023ab Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 23 Feb 2020 15:24:44 +0200 Subject: [PATCH 05/12] WIP trying to get the BB test to work --- .../monkey_zoo/blackbox/analyzers/analyzer.py | 8 +++++ .../analyzers/communication_analyzer.py | 3 +- .../analyzers/performance_analyzer.py | 36 +++++++++++++++++++ .../island_client/monkey_island_client.py | 23 +++++++++++- .../blackbox/island_configs/STRUTS2.conf | 13 ++----- envs/monkey_zoo/blackbox/test_blackbox.py | 19 ++++++++++ .../cc/resources/test/clear_caches.py | 2 +- 7 files changed, 90 insertions(+), 14 deletions(-) create mode 100644 envs/monkey_zoo/blackbox/analyzers/analyzer.py create mode 100644 envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py diff --git a/envs/monkey_zoo/blackbox/analyzers/analyzer.py b/envs/monkey_zoo/blackbox/analyzers/analyzer.py new file mode 100644 index 000000000..d6043feeb --- /dev/null +++ b/envs/monkey_zoo/blackbox/analyzers/analyzer.py @@ -0,0 +1,8 @@ +from abc import ABCMeta, abstractmethod + + +class Analyzer(object, metaclass=ABCMeta): + + @abstractmethod + def analyze_test_results(self): + raise NotImplementedError() diff --git a/envs/monkey_zoo/blackbox/analyzers/communication_analyzer.py b/envs/monkey_zoo/blackbox/analyzers/communication_analyzer.py index 491b534b8..22841f783 100644 --- a/envs/monkey_zoo/blackbox/analyzers/communication_analyzer.py +++ b/envs/monkey_zoo/blackbox/analyzers/communication_analyzer.py @@ -1,7 +1,8 @@ +from envs.monkey_zoo.blackbox.analyzers.analyzer import Analyzer from envs.monkey_zoo.blackbox.analyzers.analyzer_log import AnalyzerLog -class CommunicationAnalyzer(object): +class CommunicationAnalyzer(Analyzer): def __init__(self, island_client, machine_ips): self.island_client = island_client diff --git a/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py b/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py new file mode 100644 index 000000000..23bb5b0ae --- /dev/null +++ b/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py @@ -0,0 +1,36 @@ +from datetime import timedelta + +from envs.monkey_zoo.blackbox.analyzers.analyzer import Analyzer +from envs.monkey_zoo.blackbox.analyzers.analyzer_log import AnalyzerLog +from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient + +MAX_ALLOWED_SINGLE_PAGE_TIME = timedelta(seconds=1) +MAX_ALLOWED_TOTAL_TIME = timedelta(seconds=3) + + +class PerformanceAnalyzer(Analyzer): + + def __init__(self, island_client: MonkeyIslandClient): + self.island_client = island_client + self.log = AnalyzerLog(self.__class__.__name__) + + def analyze_test_results(self) -> bool: + self.log.clear() + total_time = timedelta() + + self.island_client.clear_caches() + timings = self.island_client.time_all_report_pages() + + single_page_time_less_then_max = True + + for page, elapsed in timings: + self.log.add_entry(f"page {page} took {str(elapsed)}") + total_time += elapsed + if elapsed > MAX_ALLOWED_SINGLE_PAGE_TIME: + single_page_time_less_then_max = False + + total_time_less_then_max = total_time < MAX_ALLOWED_TOTAL_TIME + + self.log.add_entry(f"total time is {str(total_time)}") + + return total_time_less_then_max and single_page_time_less_then_max diff --git a/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py b/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py index 53f102a72..ea68f391f 100644 --- a/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py +++ b/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py @@ -92,6 +92,27 @@ class MonkeyIslandClient(object): :raises: If error (by error code), raises the error :return: The response """ - response = self.requests.delete("api/test/clear_caches") + response = self.requests.get("api/test/clear_caches") response.raise_for_status() return response + + def time_all_report_pages(self): + REPORT_URLS = [ + "api/report/security", + "api/attack/report", + "api/report/zero_trust/findings", + "api/report/zero_trust/principles", + "api/report/zero_trust/pillars" + ] + + report_resource_to_response_time = {} + + for url in REPORT_URLS: + response = self.requests.get(url) + if response: + report_resource_to_response_time[url] = response.elapsed + else: + LOGGER.error(f"Trying to get {url} but got unexpected {str(response)}") + response.raise_for_status() + + return report_resource_to_response_time diff --git a/envs/monkey_zoo/blackbox/island_configs/STRUTS2.conf b/envs/monkey_zoo/blackbox/island_configs/STRUTS2.conf index ea53f3b0b..2f9e765a9 100644 --- a/envs/monkey_zoo/blackbox/island_configs/STRUTS2.conf +++ b/envs/monkey_zoo/blackbox/island_configs/STRUTS2.conf @@ -46,17 +46,8 @@ "exploits": { "general": { "exploiter_classes": [ - "SmbExploiter", - "WmiExploiter", - "SSHExploiter", - "ShellShockExploiter", - "SambaCryExploiter", - "ElasticGroovyExploiter", - "Struts2Exploiter", - "WebLogicExploiter", - "HadoopExploiter", - "VSFTPDExploiter" - ], + "Struts2Exploiter" + ], "skip_exploit_if_file_exist": false }, "ms08_067": { diff --git a/envs/monkey_zoo/blackbox/test_blackbox.py b/envs/monkey_zoo/blackbox/test_blackbox.py index 8581b6fbe..2d8a96dae 100644 --- a/envs/monkey_zoo/blackbox/test_blackbox.py +++ b/envs/monkey_zoo/blackbox/test_blackbox.py @@ -4,6 +4,7 @@ import logging import pytest from time import sleep +from envs.monkey_zoo.blackbox.analyzers.performance_analyzer import PerformanceAnalyzer from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient from envs.monkey_zoo.blackbox.analyzers.communication_analyzer import CommunicationAnalyzer from envs.monkey_zoo.blackbox.island_client.island_config_parser import IslandConfigParser @@ -65,6 +66,21 @@ class TestMonkeyBlackbox(object): timeout_in_seconds, log_handler).run() + @staticmethod + def run_performance_test(island_client, conf_filename, test_name, timeout_in_seconds=DEFAULT_TIMEOUT_SECONDS): + config_parser = IslandConfigParser(conf_filename) + analyzers = [ + CommunicationAnalyzer(island_client, config_parser.get_ips_of_targets()), + PerformanceAnalyzer(island_client), + ] + log_handler = TestLogsHandler(test_name, island_client, TestMonkeyBlackbox.get_log_dir_path()) + BasicTest(test_name, + island_client, + config_parser, + analyzers, + timeout_in_seconds, + log_handler).run() + @staticmethod def get_log_dir_path(): return os.path.abspath(LOG_DIR_PATH) @@ -108,3 +124,6 @@ class TestMonkeyBlackbox(object): def test_wmi_pth(self, island_client): TestMonkeyBlackbox.run_basic_test(island_client, "WMI_PTH.conf", "WMI_PTH") + + def test_performance(self, island_client): + TestMonkeyBlackbox.run_performance_test(island_client, "STRUTS2.conf", "Report_timing") diff --git a/monkey/monkey_island/cc/resources/test/clear_caches.py b/monkey/monkey_island/cc/resources/test/clear_caches.py index 1d12fb32e..f17193821 100644 --- a/monkey/monkey_island/cc/resources/test/clear_caches.py +++ b/monkey/monkey_island/cc/resources/test/clear_caches.py @@ -18,7 +18,7 @@ class ClearCaches(flask_restful.Resource): :note: DO NOT CALL THIS IN PRODUCTION CODE as this will slow down the user experience. """ @jwt_required() - def delete(self, **kw): + def get(self, **kw): try: logger.warning("Trying to clear caches! Make sure this is not production") ReportService.delete_saved_report_if_exists() From 9965947d3fc89dc93683818dc97934a5448fe234 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 23 Feb 2020 17:26:29 +0200 Subject: [PATCH 06/12] Analyzer works. now need to add setup to Terraform and add new config --- .../analyzers/performance_analyzer.py | 2 +- .../blackbox/island_configs/STRUTS2.conf | 27 +++++++------------ envs/monkey_zoo/blackbox/test_blackbox.py | 2 +- 3 files changed, 11 insertions(+), 20 deletions(-) diff --git a/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py b/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py index 23bb5b0ae..21938eb62 100644 --- a/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py +++ b/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py @@ -23,7 +23,7 @@ class PerformanceAnalyzer(Analyzer): single_page_time_less_then_max = True - for page, elapsed in timings: + for page, elapsed in timings.items(): self.log.add_entry(f"page {page} took {str(elapsed)}") total_time += elapsed if elapsed > MAX_ALLOWED_SINGLE_PAGE_TIME: diff --git a/envs/monkey_zoo/blackbox/island_configs/STRUTS2.conf b/envs/monkey_zoo/blackbox/island_configs/STRUTS2.conf index 2f9e765a9..4e487fee9 100644 --- a/envs/monkey_zoo/blackbox/island_configs/STRUTS2.conf +++ b/envs/monkey_zoo/blackbox/island_configs/STRUTS2.conf @@ -1,17 +1,8 @@ { "basic": { "credentials": { - "exploit_password_list": [ - "Password1!", - "1234", - "password", - "12345678" - ], - "exploit_user_list": [ - "Administrator", - "root", - "user" - ] + "exploit_password_list": [], + "exploit_user_list": [] }, "general": { "should_exploit": true @@ -48,7 +39,7 @@ "exploiter_classes": [ "Struts2Exploiter" ], - "skip_exploit_if_file_exist": false + "skip_exploit_if_file_exist": true }, "ms08_067": { "ms08_067_exploit_attempts": 5, @@ -140,19 +131,19 @@ "life_cycle": { "max_iterations": 1, "retry_failed_explotation": true, - "timeout_between_iterations": 100, + "timeout_between_iterations": 30, "victims_max_exploit": 7, "victims_max_find": 30 }, "system_info": { - "collect_system_info": true, - "extract_azure_creds": true, - "should_use_mimikatz": true + "collect_system_info": false, + "extract_azure_creds": false, + "should_use_mimikatz": false } }, "network": { "ping_scanner": { - "ping_scan_timeout": 1000 + "ping_scan_timeout": 100 }, "tcp_scanner": { "HTTP_PORTS": [ @@ -164,7 +155,7 @@ ], "tcp_scan_get_banner": true, "tcp_scan_interval": 0, - "tcp_scan_timeout": 3000, + "tcp_scan_timeout": 300, "tcp_target_ports": [ 22, 2222, diff --git a/envs/monkey_zoo/blackbox/test_blackbox.py b/envs/monkey_zoo/blackbox/test_blackbox.py index 2d8a96dae..71da9381b 100644 --- a/envs/monkey_zoo/blackbox/test_blackbox.py +++ b/envs/monkey_zoo/blackbox/test_blackbox.py @@ -70,7 +70,7 @@ class TestMonkeyBlackbox(object): def run_performance_test(island_client, conf_filename, test_name, timeout_in_seconds=DEFAULT_TIMEOUT_SECONDS): config_parser = IslandConfigParser(conf_filename) analyzers = [ - CommunicationAnalyzer(island_client, config_parser.get_ips_of_targets()), + # TODO CommunicationAnalyzer(island_client, config_parser.get_ips_of_targets()), PerformanceAnalyzer(island_client), ] log_handler = TestLogsHandler(test_name, island_client, TestMonkeyBlackbox.get_log_dir_path()) From 97976cdbc5eb0acfebedff981b4a687b7570154c Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Tue, 25 Feb 2020 11:24:28 +0200 Subject: [PATCH 07/12] Got 500 from delete operation so simplyfing and re-trying --- .../analyzers/performance_analyzer.py | 18 +- .../island_client/monkey_island_client.py | 8 +- .../blackbox/island_configs/PERFORMANCE.conf | 186 ++++++++++++++++++ envs/monkey_zoo/blackbox/test_blackbox.py | 49 +++-- envs/monkey_zoo/blackbox/tests/basic_test.py | 23 ++- .../cc/services/attack/attack_report.py | 14 +- .../cc/services/reporting/report.py | 15 +- 7 files changed, 274 insertions(+), 39 deletions(-) create mode 100644 envs/monkey_zoo/blackbox/island_configs/PERFORMANCE.conf diff --git a/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py b/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py index 21938eb62..5f082211e 100644 --- a/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py +++ b/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py @@ -1,3 +1,4 @@ +import logging from datetime import timedelta from envs.monkey_zoo.blackbox.analyzers.analyzer import Analyzer @@ -7,15 +8,23 @@ from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIs MAX_ALLOWED_SINGLE_PAGE_TIME = timedelta(seconds=1) MAX_ALLOWED_TOTAL_TIME = timedelta(seconds=3) +logger = logging.getLogger(__name__) + class PerformanceAnalyzer(Analyzer): - def __init__(self, island_client: MonkeyIslandClient): + def __init__(self, island_client: MonkeyIslandClient, break_if_took_too_long=False): + self.break_if_took_too_long = break_if_took_too_long self.island_client = island_client self.log = AnalyzerLog(self.__class__.__name__) def analyze_test_results(self) -> bool: self.log.clear() + + if not self.island_client.is_all_monkeys_dead(): + self.log.add_entry("Can't test report times since not all Monkeys have died.") + return False + total_time = timedelta() self.island_client.clear_caches() @@ -33,4 +42,11 @@ class PerformanceAnalyzer(Analyzer): self.log.add_entry(f"total time is {str(total_time)}") + if self.break_if_took_too_long and (not (total_time_less_then_max and single_page_time_less_then_max)): + logger.warning( + "Calling breakpoint - pausing to enable investigation of island. Type 'c' to continue once you're done " + "investigating. type 'p timings' and 'p total_time' to see performance information." + ) + breakpoint() + return total_time_less_then_max and single_page_time_less_then_max diff --git a/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py b/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py index ea68f391f..a280d79a9 100644 --- a/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py +++ b/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py @@ -1,3 +1,4 @@ +from datetime import timedelta from time import sleep import json @@ -109,10 +110,13 @@ class MonkeyIslandClient(object): for url in REPORT_URLS: response = self.requests.get(url) - if response: + if response.ok: + LOGGER.debug(f"Got ok for {url} content peek:\n{response.content[:120]}") report_resource_to_response_time[url] = response.elapsed else: LOGGER.error(f"Trying to get {url} but got unexpected {str(response)}") - response.raise_for_status() + # instead of raising for status, mark failed responses as maxtime + report_resource_to_response_time[url] = timedelta.max() + return report_resource_to_response_time diff --git a/envs/monkey_zoo/blackbox/island_configs/PERFORMANCE.conf b/envs/monkey_zoo/blackbox/island_configs/PERFORMANCE.conf new file mode 100644 index 000000000..ebe3d8814 --- /dev/null +++ b/envs/monkey_zoo/blackbox/island_configs/PERFORMANCE.conf @@ -0,0 +1,186 @@ +{ + "basic": { + "credentials": { + "exploit_password_list": [ + "Password1!", + "12345678", + "^NgDvY59~8" + ], + "exploit_user_list": [ + "Administrator", + "m0nk3y", + "user" + ] + }, + "general": { + "should_exploit": true + } + }, + "basic_network": { + "general": { + "blocked_ips": [], + "depth": 2, + "local_network_scan": false, + "subnet_scan_list": [ + "10.2.2.2", + "10.2.2.4" + ] + }, + "network_analysis": { + "inaccessible_subnets": [] + } + }, + "cnc": { + "servers": { + "command_servers": [ + "10.2.2.251:5000" + ], + "current_server": "10.2.2.251:5000", + "internet_services": [ + "monkey.guardicore.com", + "www.google.com" + ] + } + }, + "exploits": { + "general": { + "exploiter_classes": [ + "SSHExploiter", + "MSSQLExploiter", + "ElasticGroovyExploiter", + "HadoopExploiter" + ], + "skip_exploit_if_file_exist": false + }, + "ms08_067": { + "ms08_067_exploit_attempts": 5, + "remote_user_pass": "Password1!", + "user_to_add": "Monkey_IUSER_SUPPORT" + }, + "rdp_grinder": { + "rdp_use_vbs_download": true + }, + "sambacry": { + "sambacry_folder_paths_to_guess": [ + "/", + "/mnt", + "/tmp", + "/storage", + "/export", + "/share", + "/shares", + "/home" + ], + "sambacry_shares_not_to_check": [ + "IPC$", + "print$" + ], + "sambacry_trigger_timeout": 5 + }, + "smb_service": { + "smb_download_timeout": 300, + "smb_service_name": "InfectionMonkey" + } + }, + "internal": { + "classes": { + "finger_classes": [ + "SMBFinger", + "SSHFinger", + "PingScanner", + "HTTPFinger", + "MySQLFinger", + "MSSQLFinger", + "ElasticFinger" + ] + }, + "dropper": { + "dropper_date_reference_path_linux": "/bin/sh", + "dropper_date_reference_path_windows": "%windir%\\system32\\kernel32.dll", + "dropper_set_date": true, + "dropper_target_path_linux": "/tmp/monkey", + "dropper_target_path_win_32": "C:\\Windows\\temp\\monkey32.exe", + "dropper_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe", + "dropper_try_move_first": true + }, + "exploits": { + "exploit_lm_hash_list": [], + "exploit_ntlm_hash_list": [], + "exploit_ssh_keys": [] + }, + "general": { + "keep_tunnel_open_time": 1, + "monkey_dir_name": "monkey_dir", + "singleton_mutex_name": "{2384ec59-0df8-4ab9-918c-843740924a28}" + }, + "kill_file": { + "kill_file_path_linux": "/var/run/monkey.not", + "kill_file_path_windows": "%windir%\\monkey.not" + }, + "logging": { + "dropper_log_path_linux": "/tmp/user-1562", + "dropper_log_path_windows": "%temp%\\~df1562.tmp", + "monkey_log_path_linux": "/tmp/user-1563", + "monkey_log_path_windows": "%temp%\\~df1563.tmp", + "send_log_to_server": true + } + }, + "monkey": { + "behaviour": { + "PBA_linux_filename": "", + "PBA_windows_filename": "", + "custom_PBA_linux_cmd": "", + "custom_PBA_windows_cmd": "", + "self_delete_in_cleanup": true, + "serialize_config": false, + "use_file_logging": true + }, + "general": { + "alive": true, + "post_breach_actions": [] + }, + "life_cycle": { + "max_iterations": 1, + "retry_failed_explotation": true, + "timeout_between_iterations": 100, + "victims_max_exploit": 7, + "victims_max_find": 30 + }, + "system_info": { + "collect_system_info": true, + "extract_azure_creds": false, + "should_use_mimikatz": true + } + }, + "network": { + "ping_scanner": { + "ping_scan_timeout": 500 + }, + "tcp_scanner": { + "HTTP_PORTS": [ + 80, + 8080, + 443, + 8008, + 7001 + ], + "tcp_scan_get_banner": true, + "tcp_scan_interval": 0, + "tcp_scan_timeout": 1000, + "tcp_target_ports": [ + 22, + 2222, + 445, + 135, + 3389, + 80, + 8080, + 443, + 8008, + 3306, + 9200, + 7001 + ] + } + } +} diff --git a/envs/monkey_zoo/blackbox/test_blackbox.py b/envs/monkey_zoo/blackbox/test_blackbox.py index 71da9381b..d2e98f2de 100644 --- a/envs/monkey_zoo/blackbox/test_blackbox.py +++ b/envs/monkey_zoo/blackbox/test_blackbox.py @@ -13,6 +13,7 @@ from envs.monkey_zoo.blackbox.tests.basic_test import BasicTest from envs.monkey_zoo.blackbox.log_handlers.test_logs_handler import TestLogsHandler DEFAULT_TIMEOUT_SECONDS = 5*60 +PERFORMANCE_TIMEOUT_SECONDS = 10*60 MACHINE_BOOTUP_WAIT_SECONDS = 30 GCP_TEST_MACHINE_LIST = ['sshkeys-11', 'sshkeys-12', 'elastic-4', 'elastic-5', 'hadoop-2', 'hadoop-3', 'mssql-16', 'mimikatz-14', 'mimikatz-15', 'struts2-23', 'struts2-24', 'tunneling-9', 'tunneling-10', @@ -59,27 +60,30 @@ class TestMonkeyBlackbox(object): config_parser = IslandConfigParser(conf_filename) analyzer = CommunicationAnalyzer(island_client, config_parser.get_ips_of_targets()) log_handler = TestLogsHandler(test_name, island_client, TestMonkeyBlackbox.get_log_dir_path()) - BasicTest(test_name, - island_client, - config_parser, - [analyzer], - timeout_in_seconds, - log_handler).run() + BasicTest( + name=test_name, + island_client=island_client, + config_parser=config_parser, + analyzers=[analyzer], + timeout=timeout_in_seconds, + post_exec_analyzers=[], + log_handler=log_handler).run() @staticmethod def run_performance_test(island_client, conf_filename, test_name, timeout_in_seconds=DEFAULT_TIMEOUT_SECONDS): config_parser = IslandConfigParser(conf_filename) - analyzers = [ - # TODO CommunicationAnalyzer(island_client, config_parser.get_ips_of_targets()), - PerformanceAnalyzer(island_client), - ] log_handler = TestLogsHandler(test_name, island_client, TestMonkeyBlackbox.get_log_dir_path()) - BasicTest(test_name, - island_client, - config_parser, - analyzers, - timeout_in_seconds, - log_handler).run() + BasicTest( + name=test_name, + island_client=island_client, + config_parser=config_parser, + analyzers=[CommunicationAnalyzer(island_client, config_parser.get_ips_of_targets())], + timeout=timeout_in_seconds, + post_exec_analyzers=[PerformanceAnalyzer( + island_client, + break_if_took_too_long=True # TODO change to false before merging!!! + )], + log_handler=log_handler).run() @staticmethod def get_log_dir_path(): @@ -126,4 +130,15 @@ class TestMonkeyBlackbox(object): TestMonkeyBlackbox.run_basic_test(island_client, "WMI_PTH.conf", "WMI_PTH") def test_performance(self, island_client): - TestMonkeyBlackbox.run_performance_test(island_client, "STRUTS2.conf", "Report_timing") + """ + This test includes the SSH + Elastic + Hadoop + MSSQL machines all in one test + for a total of 8 machines including the Monkey Island. + + Is has 2 analyzers - the regular one which checks all the Monkeys + and the Timing one which checks how long the report took to execute + """ + TestMonkeyBlackbox.run_performance_test( + island_client, + "PERFORMANCE.conf", + "test_report_performance", + timeout_in_seconds=PERFORMANCE_TIMEOUT_SECONDS) diff --git a/envs/monkey_zoo/blackbox/tests/basic_test.py b/envs/monkey_zoo/blackbox/tests/basic_test.py index 8456dccad..cad7f28d7 100644 --- a/envs/monkey_zoo/blackbox/tests/basic_test.py +++ b/envs/monkey_zoo/blackbox/tests/basic_test.py @@ -14,11 +14,12 @@ LOGGER = logging.getLogger(__name__) class BasicTest(object): - def __init__(self, name, island_client, config_parser, analyzers, timeout, log_handler): + def __init__(self, name, island_client, config_parser, analyzers, timeout, post_exec_analyzers, log_handler): self.name = name self.island_client = island_client self.config_parser = config_parser self.analyzers = analyzers + self.post_exec_analyzers = post_exec_analyzers self.timeout = timeout self.log_handler = log_handler @@ -32,13 +33,13 @@ class BasicTest(object): self.island_client.kill_all_monkeys() self.wait_until_monkeys_die() self.wait_for_monkey_process_to_finish() + self.test_post_exec_analyzers() self.parse_logs() self.island_client.reset_env() def print_test_starting_info(self): LOGGER.info("Started {} test".format(self.name)) - LOGGER.info("Machines participating in test:") - LOGGER.info(" ".join(self.config_parser.get_ips_of_targets())) + LOGGER.info("Machines participating in test: " + ", ".join(self.config_parser.get_ips_of_targets())) print("") def test_until_timeout(self): @@ -62,14 +63,12 @@ class BasicTest(object): timer.get_time_taken())) def all_analyzers_pass(self): - for analyzer in self.analyzers: - if not analyzer.analyze_test_results(): - return False - return True + analyzers_results = [analyzer.analyze_test_results() for analyzer in self.analyzers] + return all(analyzers_results) def get_analyzer_logs(self): log = "" - for analyzer in self.analyzers: + for analyzer in self.get_all_analyzers(): log += "\n" + analyzer.log.get_contents() return log @@ -94,4 +93,12 @@ class BasicTest(object): If we try to launch monkey during that time window monkey will fail to start, that's why test needs to wait a bit even after all monkeys are dead. """ + LOGGER.debug() sleep(TIME_FOR_MONKEY_PROCESS_TO_FINISH) + + def test_post_exec_analyzers(self): + post_exec_analyzers_results = [analyzer.analyze_test_results() for analyzer in self.post_exec_analyzers] + assert all(post_exec_analyzers_results) + + def get_all_analyzers(self): + return self.analyzers + self.post_exec_analyzers diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py index 2f1e9dcf8..bc04a4854 100644 --- a/monkey/monkey_island/cc/services/attack/attack_report.py +++ b/monkey/monkey_island/cc/services/attack/attack_report.py @@ -106,8 +106,12 @@ class AttackReportService: @staticmethod def delete_saved_report_if_exists(): - if AttackReportService.is_report_generated(): - latest_report = mongo.db.attack_report.find_one({'name': REPORT_NAME}) - delete_result = mongo.db.report.delete_one({"_id": latest_report['_id']}) - if delete_result.deleted_count != 1: - raise RuntimeError("Error while deleting report:" + str(delete_result)) + delete_result = mongo.db.attack_report.delete_many({}) + if mongo.db.attack_report.count_documents({}) != 0: + raise RuntimeError("Attack Report cache not cleared. DeleteResult: " + delete_result.raw_result) + # if AttackReportService.is_report_generated(): + # mongo.db.attack_report.delete_many({}) + # latest_report = mongo.db.attack_report.find_one({'name': REPORT_NAME}) + # delete_result = mongo.db.report.delete_one({"_id": latest_report['_id']}) + # if delete_result.deleted_count != 1: + # raise RuntimeError("Error while deleting report. Deleted count: " + str(delete_result.deleted_count)) diff --git a/monkey/monkey_island/cc/services/reporting/report.py b/monkey/monkey_island/cc/services/reporting/report.py index f96279dd6..8e08f33d1 100644 --- a/monkey/monkey_island/cc/services/reporting/report.py +++ b/monkey/monkey_island/cc/services/reporting/report.py @@ -779,12 +779,15 @@ class ReportService: This function clears the saved report from the DB. :raises RuntimeError if deletion failed """ - latest_report_doc = mongo.db.report.find_one({}, {'meta.latest_monkey_modifytime': 1}) - - if latest_report_doc: - delete_result = mongo.db.report.delete_one({"_id": latest_report_doc['_id']}) - if delete_result.deleted_count != 1: - raise RuntimeError("Error while deleting report:" + str(delete_result)) + delete_result = mongo.db.report.delete_many({}) + if mongo.db.report.count_documents({}) != 0: + raise RuntimeError("Report cache not cleared. DeleteResult: " + delete_result.raw_result) + # latest_report_doc = mongo.db.report.find_one({}, {'meta.latest_monkey_modifytime': 1}) + # + # if latest_report_doc: + # delete_result = mongo.db.report.delete_one({"_id": latest_report_doc['_id']}) + # if delete_result.deleted_count != 1: + # raise RuntimeError("Error while deleting report:" + str(delete_result)) @staticmethod def decode_dot_char_before_mongo_insert(report_dict): From e815ac53dac85f9c27bbea7bccdb276a8e8537b8 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Tue, 25 Feb 2020 12:19:57 +0200 Subject: [PATCH 08/12] Changed the post_exec analyzer to not work with Analyzer log but rather work with regular log --- .../analyzers/performance_analyzer.py | 22 +++++++++---------- .../island_client/monkey_island_client.py | 7 ++++-- envs/monkey_zoo/blackbox/tests/basic_test.py | 8 ++----- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py b/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py index 5f082211e..d6b3e23f0 100644 --- a/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py +++ b/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py @@ -2,11 +2,10 @@ import logging from datetime import timedelta from envs.monkey_zoo.blackbox.analyzers.analyzer import Analyzer -from envs.monkey_zoo.blackbox.analyzers.analyzer_log import AnalyzerLog from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient -MAX_ALLOWED_SINGLE_PAGE_TIME = timedelta(seconds=1) -MAX_ALLOWED_TOTAL_TIME = timedelta(seconds=3) +MAX_ALLOWED_SINGLE_PAGE_TIME = timedelta(seconds=2) +MAX_ALLOWED_TOTAL_TIME = timedelta(seconds=5) logger = logging.getLogger(__name__) @@ -16,13 +15,10 @@ class PerformanceAnalyzer(Analyzer): def __init__(self, island_client: MonkeyIslandClient, break_if_took_too_long=False): self.break_if_took_too_long = break_if_took_too_long self.island_client = island_client - self.log = AnalyzerLog(self.__class__.__name__) def analyze_test_results(self) -> bool: - self.log.clear() - if not self.island_client.is_all_monkeys_dead(): - self.log.add_entry("Can't test report times since not all Monkeys have died.") + logger.info("Can't test report times since not all Monkeys have died.") return False total_time = timedelta() @@ -33,20 +29,22 @@ class PerformanceAnalyzer(Analyzer): single_page_time_less_then_max = True for page, elapsed in timings.items(): - self.log.add_entry(f"page {page} took {str(elapsed)}") + logger.info(f"page {page} took {str(elapsed)}") total_time += elapsed if elapsed > MAX_ALLOWED_SINGLE_PAGE_TIME: single_page_time_less_then_max = False total_time_less_then_max = total_time < MAX_ALLOWED_TOTAL_TIME - self.log.add_entry(f"total time is {str(total_time)}") + logger.info(f"total time is {str(total_time)}") - if self.break_if_took_too_long and (not (total_time_less_then_max and single_page_time_less_then_max)): + performance_is_good_enough = total_time_less_then_max and single_page_time_less_then_max + + if self.break_if_took_too_long and not performance_is_good_enough: logger.warning( "Calling breakpoint - pausing to enable investigation of island. Type 'c' to continue once you're done " - "investigating. type 'p timings' and 'p total_time' to see performance information." + "investigating. Type 'p timings' and 'p total_time' to see performance information." ) breakpoint() - return total_time_less_then_max and single_page_time_less_then_max + return performance_is_good_enough diff --git a/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py b/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py index a280d79a9..193f1473c 100644 --- a/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py +++ b/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py @@ -98,6 +98,10 @@ class MonkeyIslandClient(object): return response def time_all_report_pages(self): + """ + Calculates elapsed time of request for each report URL + Make sure to call clear_caches before this function if you want to measure "worst case" generation time. + """ REPORT_URLS = [ "api/report/security", "api/attack/report", @@ -111,12 +115,11 @@ class MonkeyIslandClient(object): for url in REPORT_URLS: response = self.requests.get(url) if response.ok: - LOGGER.debug(f"Got ok for {url} content peek:\n{response.content[:120]}") + LOGGER.debug(f"Got ok for {url} content peek:\n{response.content[:120].strip()}") report_resource_to_response_time[url] = response.elapsed else: LOGGER.error(f"Trying to get {url} but got unexpected {str(response)}") # instead of raising for status, mark failed responses as maxtime report_resource_to_response_time[url] = timedelta.max() - return report_resource_to_response_time diff --git a/envs/monkey_zoo/blackbox/tests/basic_test.py b/envs/monkey_zoo/blackbox/tests/basic_test.py index cad7f28d7..a5e71c64c 100644 --- a/envs/monkey_zoo/blackbox/tests/basic_test.py +++ b/envs/monkey_zoo/blackbox/tests/basic_test.py @@ -1,4 +1,3 @@ -import json from time import sleep import logging @@ -68,7 +67,7 @@ class BasicTest(object): def get_analyzer_logs(self): log = "" - for analyzer in self.get_all_analyzers(): + for analyzer in self.analyzers: log += "\n" + analyzer.log.get_contents() return log @@ -93,12 +92,9 @@ class BasicTest(object): If we try to launch monkey during that time window monkey will fail to start, that's why test needs to wait a bit even after all monkeys are dead. """ - LOGGER.debug() + LOGGER.debug("Waiting for Monkey process to close...") sleep(TIME_FOR_MONKEY_PROCESS_TO_FINISH) def test_post_exec_analyzers(self): post_exec_analyzers_results = [analyzer.analyze_test_results() for analyzer in self.post_exec_analyzers] assert all(post_exec_analyzers_results) - - def get_all_analyzers(self): - return self.analyzers + self.post_exec_analyzers From afbc13a06b1d263fc65758a18bbc457bdfc894f9 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Tue, 25 Feb 2020 14:57:50 +0200 Subject: [PATCH 09/12] CR fixes --- .../analyzers/performance_analyzer.py | 17 +++++++++++++++-- .../island_client/monkey_island_client.py | 19 +++++++++++-------- envs/monkey_zoo/blackbox/test_blackbox.py | 5 ++--- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py b/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py index d6b3e23f0..8007f8cdd 100644 --- a/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py +++ b/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py @@ -7,6 +7,14 @@ from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIs MAX_ALLOWED_SINGLE_PAGE_TIME = timedelta(seconds=2) MAX_ALLOWED_TOTAL_TIME = timedelta(seconds=5) +REPORT_URLS = [ + "api/report/security", + "api/attack/report", + "api/report/zero_trust/findings", + "api/report/zero_trust/principles", + "api/report/zero_trust/pillars" +] + logger = logging.getLogger(__name__) @@ -18,12 +26,17 @@ class PerformanceAnalyzer(Analyzer): def analyze_test_results(self) -> bool: if not self.island_client.is_all_monkeys_dead(): - logger.info("Can't test report times since not all Monkeys have died.") - return False + raise RuntimeError("Can't test report times since not all Monkeys have died.") total_time = timedelta() self.island_client.clear_caches() + + report_resource_to_response_time = {} + + for url in REPORT_URLS: + report_resource_to_response_time[url] = self.island_client.get_elapsed_for_get_request(url) + timings = self.island_client.time_all_report_pages() single_page_time_less_then_max = True diff --git a/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py b/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py index 193f1473c..2f838cec6 100644 --- a/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py +++ b/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py @@ -113,13 +113,16 @@ class MonkeyIslandClient(object): report_resource_to_response_time = {} for url in REPORT_URLS: - response = self.requests.get(url) - if response.ok: - LOGGER.debug(f"Got ok for {url} content peek:\n{response.content[:120].strip()}") - report_resource_to_response_time[url] = response.elapsed - else: - LOGGER.error(f"Trying to get {url} but got unexpected {str(response)}") - # instead of raising for status, mark failed responses as maxtime - report_resource_to_response_time[url] = timedelta.max() + report_resource_to_response_time[url] = self.get_elapsed_for_get_request(url) return report_resource_to_response_time + + def get_elapsed_for_get_request(self, url): + response = self.requests.get(url) + if response.ok: + LOGGER.debug(f"Got ok for {url} content peek:\n{response.content[:120].strip()}") + return response.elapsed + else: + LOGGER.error(f"Trying to get {url} but got unexpected {str(response)}") + # instead of raising for status, mark failed responses as maxtime + return timedelta.max() diff --git a/envs/monkey_zoo/blackbox/test_blackbox.py b/envs/monkey_zoo/blackbox/test_blackbox.py index d2e98f2de..03e4d03c5 100644 --- a/envs/monkey_zoo/blackbox/test_blackbox.py +++ b/envs/monkey_zoo/blackbox/test_blackbox.py @@ -13,7 +13,6 @@ from envs.monkey_zoo.blackbox.tests.basic_test import BasicTest from envs.monkey_zoo.blackbox.log_handlers.test_logs_handler import TestLogsHandler DEFAULT_TIMEOUT_SECONDS = 5*60 -PERFORMANCE_TIMEOUT_SECONDS = 10*60 MACHINE_BOOTUP_WAIT_SECONDS = 30 GCP_TEST_MACHINE_LIST = ['sshkeys-11', 'sshkeys-12', 'elastic-4', 'elastic-5', 'hadoop-2', 'hadoop-3', 'mssql-16', 'mimikatz-14', 'mimikatz-15', 'struts2-23', 'struts2-24', 'tunneling-9', 'tunneling-10', @@ -70,7 +69,7 @@ class TestMonkeyBlackbox(object): log_handler=log_handler).run() @staticmethod - def run_performance_test(island_client, conf_filename, test_name, timeout_in_seconds=DEFAULT_TIMEOUT_SECONDS): + def run_performance_test(island_client, conf_filename, test_name, timeout_in_seconds): config_parser = IslandConfigParser(conf_filename) log_handler = TestLogsHandler(test_name, island_client, TestMonkeyBlackbox.get_log_dir_path()) BasicTest( @@ -141,4 +140,4 @@ class TestMonkeyBlackbox(object): island_client, "PERFORMANCE.conf", "test_report_performance", - timeout_in_seconds=PERFORMANCE_TIMEOUT_SECONDS) + timeout_in_seconds=10*60) From ddd89c2a1462ab1790754830a8a63a85f323b7f4 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 4 Mar 2020 15:03:08 +0200 Subject: [PATCH 10/12] Deleted commented out code --- monkey/monkey_island/cc/services/attack/attack_report.py | 6 ------ monkey/monkey_island/cc/services/reporting/report.py | 6 ------ 2 files changed, 12 deletions(-) diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py index bc04a4854..7184ce202 100644 --- a/monkey/monkey_island/cc/services/attack/attack_report.py +++ b/monkey/monkey_island/cc/services/attack/attack_report.py @@ -109,9 +109,3 @@ class AttackReportService: delete_result = mongo.db.attack_report.delete_many({}) if mongo.db.attack_report.count_documents({}) != 0: raise RuntimeError("Attack Report cache not cleared. DeleteResult: " + delete_result.raw_result) - # if AttackReportService.is_report_generated(): - # mongo.db.attack_report.delete_many({}) - # latest_report = mongo.db.attack_report.find_one({'name': REPORT_NAME}) - # delete_result = mongo.db.report.delete_one({"_id": latest_report['_id']}) - # if delete_result.deleted_count != 1: - # raise RuntimeError("Error while deleting report. Deleted count: " + str(delete_result.deleted_count)) diff --git a/monkey/monkey_island/cc/services/reporting/report.py b/monkey/monkey_island/cc/services/reporting/report.py index 8e08f33d1..f2c763d23 100644 --- a/monkey/monkey_island/cc/services/reporting/report.py +++ b/monkey/monkey_island/cc/services/reporting/report.py @@ -782,12 +782,6 @@ class ReportService: delete_result = mongo.db.report.delete_many({}) if mongo.db.report.count_documents({}) != 0: raise RuntimeError("Report cache not cleared. DeleteResult: " + delete_result.raw_result) - # latest_report_doc = mongo.db.report.find_one({}, {'meta.latest_monkey_modifytime': 1}) - # - # if latest_report_doc: - # delete_result = mongo.db.report.delete_one({"_id": latest_report_doc['_id']}) - # if delete_result.deleted_count != 1: - # raise RuntimeError("Error while deleting report:" + str(delete_result)) @staticmethod def decode_dot_char_before_mongo_insert(report_dict): From 31c348d26adec33d937869fcb802aab8981f1dad Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 15 Mar 2020 16:58:49 +0200 Subject: [PATCH 11/12] Update test_blackbox.py --- envs/monkey_zoo/blackbox/test_blackbox.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/envs/monkey_zoo/blackbox/test_blackbox.py b/envs/monkey_zoo/blackbox/test_blackbox.py index 03e4d03c5..e2da6a992 100644 --- a/envs/monkey_zoo/blackbox/test_blackbox.py +++ b/envs/monkey_zoo/blackbox/test_blackbox.py @@ -80,7 +80,7 @@ class TestMonkeyBlackbox(object): timeout=timeout_in_seconds, post_exec_analyzers=[PerformanceAnalyzer( island_client, - break_if_took_too_long=True # TODO change to false before merging!!! + break_if_took_too_long=False )], log_handler=log_handler).run() From 347941c776c737aecaf177c838af5a904a0d15ea Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 16 Mar 2020 14:31:13 +0200 Subject: [PATCH 12/12] Delete unused function --- .../analyzers/performance_analyzer.py | 12 ++++------- .../island_client/monkey_island_client.py | 20 ------------------- 2 files changed, 4 insertions(+), 28 deletions(-) diff --git a/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py b/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py index 8007f8cdd..3e1c48199 100644 --- a/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py +++ b/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py @@ -28,20 +28,16 @@ class PerformanceAnalyzer(Analyzer): if not self.island_client.is_all_monkeys_dead(): raise RuntimeError("Can't test report times since not all Monkeys have died.") - total_time = timedelta() - + # Collect timings for all pages self.island_client.clear_caches() - report_resource_to_response_time = {} - for url in REPORT_URLS: report_resource_to_response_time[url] = self.island_client.get_elapsed_for_get_request(url) - timings = self.island_client.time_all_report_pages() - + # Calculate total time and check each page single_page_time_less_then_max = True - - for page, elapsed in timings.items(): + total_time = timedelta() + for page, elapsed in report_resource_to_response_time.items(): logger.info(f"page {page} took {str(elapsed)}") total_time += elapsed if elapsed > MAX_ALLOWED_SINGLE_PAGE_TIME: diff --git a/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py b/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py index 2f838cec6..27a54222b 100644 --- a/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py +++ b/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py @@ -97,26 +97,6 @@ class MonkeyIslandClient(object): response.raise_for_status() return response - def time_all_report_pages(self): - """ - Calculates elapsed time of request for each report URL - Make sure to call clear_caches before this function if you want to measure "worst case" generation time. - """ - REPORT_URLS = [ - "api/report/security", - "api/attack/report", - "api/report/zero_trust/findings", - "api/report/zero_trust/principles", - "api/report/zero_trust/pillars" - ] - - report_resource_to_response_time = {} - - for url in REPORT_URLS: - report_resource_to_response_time[url] = self.get_elapsed_for_get_request(url) - - return report_resource_to_response_time - def get_elapsed_for_get_request(self, url): response = self.requests.get(url) if response.ok: