diff --git a/envs/monkey_zoo/.gitignore b/envs/monkey_zoo/.gitignore index e7549cdec..333c1e910 100644 --- a/envs/monkey_zoo/.gitignore +++ b/envs/monkey_zoo/.gitignore @@ -1,2 +1 @@ logs/ -/blackbox/tests/performance/telemetry_sample diff --git a/envs/monkey_zoo/blackbox/README.md b/envs/monkey_zoo/blackbox/README.md index a1cb95962..4957b1f4e 100644 --- a/envs/monkey_zoo/blackbox/README.md +++ b/envs/monkey_zoo/blackbox/README.md @@ -16,8 +16,6 @@ Either run pytest from `/monkey` directory or set `PYTHONPATH` environment varia Blackbox tests have following parameters: - `--island=IP` Sets island's IP - `--no-gcp` (Optional) Use for no interaction with the cloud (local test). -- `--quick-performance-tests` (Optional) If enabled performance tests won't reset island and won't send telemetries, -instead will just test performance of endpoints in already present island state. Example run command: @@ -26,26 +24,3 @@ Example run command: #### Running in PyCharm Configure a PyTest configuration with the additional arguments `-s --island=35.207.152.72:5000`, and to run from directory `monkey\envs\monkey_zoo\blackbox`. - -### Running telemetry performance test - -**Before running performance test make sure browser is not sending requests to island!** - -To run telemetry performance test follow these steps: -0. Set no password protection on the island. -Make sure the island parameter is an IP address(not localhost) as the name resolution will increase the time for requests. -1. Gather monkey telemetries. - 1. Enable "Export monkey telemetries" in Configuration -> Internal -> Tests if you don't have - exported telemetries already. - 2. Run monkey and wait until infection is done. - 3. All telemetries are gathered in `monkey/telem_sample`. If not, restart the island process. -2. Run telemetry performance test. - 1. Move directory `monkey/telem_sample` to `envs/monkey_zoo/blackbox/tests/performance/telemetry_sample` - 2. (Optional) Use `envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/sample_multiplier.py` to multiply - telemetries gathered. - 1. Run `sample_multiplier.py` script with working directory set to `monkey\envs\monkey_zoo\blackbox` - 2. Pass integer to indicate the multiplier. For example running `telem_parser.py 4` will replicate - telemetries 4 times. - 3. If you're using pycharm check "Emulate terminal in output console" on debug/run configuration. - 3. Add a `--run-performance-tests` flag to blackbox scripts to run performance tests as part of BlackBox tests. - You can run a single test separately by adding `-k 'test_telem_performance'` option. diff --git a/envs/monkey_zoo/blackbox/analyzers/communication_analyzer.py b/envs/monkey_zoo/blackbox/analyzers/communication_analyzer.py index 9f43bee7c..055de96a5 100644 --- a/envs/monkey_zoo/blackbox/analyzers/communication_analyzer.py +++ b/envs/monkey_zoo/blackbox/analyzers/communication_analyzer.py @@ -1,9 +1,12 @@ +from typing import Iterable + 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 class CommunicationAnalyzer(Analyzer): - def __init__(self, island_client, machine_ips): + def __init__(self, island_client: MonkeyIslandClient, machine_ips: Iterable[str]): self.island_client = island_client self.machine_ips = machine_ips self.log = AnalyzerLog(self.__class__.__name__) @@ -19,6 +22,6 @@ class CommunicationAnalyzer(Analyzer): self.log.add_entry("Monkey from {} communicated back".format(machine_ip)) return all_monkeys_communicated - def did_monkey_communicate_back(self, machine_ip): + def did_monkey_communicate_back(self, machine_ip: str): query = {"ip_addresses": {"$elemMatch": {"$eq": machine_ip}}} return len(self.island_client.find_monkeys_in_db(query)) > 0 diff --git a/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py b/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py deleted file mode 100644 index 30e635652..000000000 --- a/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py +++ /dev/null @@ -1,50 +0,0 @@ -import logging -from datetime import timedelta -from typing import Dict - -from envs.monkey_zoo.blackbox.analyzers.analyzer import Analyzer -from envs.monkey_zoo.blackbox.tests.performance.performance_test_config import PerformanceTestConfig - -LOGGER = logging.getLogger(__name__) - - -class PerformanceAnalyzer(Analyzer): - def __init__( - self, performance_test_config: PerformanceTestConfig, endpoint_timings: Dict[str, timedelta] - ): - self.performance_test_config = performance_test_config - self.endpoint_timings = endpoint_timings - - def analyze_test_results(self): - # Calculate total time and check each endpoint - single_page_time_less_then_max = True - total_time = timedelta() - for endpoint, elapsed in self.endpoint_timings.items(): - total_time += elapsed - if elapsed > self.performance_test_config.max_allowed_single_page_time: - single_page_time_less_then_max = False - - total_time_less_then_max = total_time < self.performance_test_config.max_allowed_total_time - - PerformanceAnalyzer.log_slowest_endpoints(self.endpoint_timings) - LOGGER.info(f"Total time is {str(total_time)}") - - performance_is_good_enough = total_time_less_then_max and single_page_time_less_then_max - - if self.performance_test_config.break_on_timeout 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." - ) - breakpoint() - - return performance_is_good_enough - - @staticmethod - def log_slowest_endpoints(endpoint_timings, max_endpoints_to_display=100): - slow_endpoint_list = list(endpoint_timings.items()) - slow_endpoint_list.sort(key=lambda x: x[1], reverse=True) - slow_endpoint_list = slow_endpoint_list[:max_endpoints_to_display] - for endpoint in slow_endpoint_list: - LOGGER.info(f"{endpoint[0]} took {str(endpoint[1])}") diff --git a/envs/monkey_zoo/blackbox/analyzers/zerologon_analyzer.py b/envs/monkey_zoo/blackbox/analyzers/zerologon_analyzer.py index 5762a0315..e279f85b2 100644 --- a/envs/monkey_zoo/blackbox/analyzers/zerologon_analyzer.py +++ b/envs/monkey_zoo/blackbox/analyzers/zerologon_analyzer.py @@ -1,9 +1,6 @@ from pprint import pformat from typing import List -import dpath.util - -from common.config_value_paths import LM_HASH_LIST_PATH, NTLM_HASH_LIST_PATH, USER_LIST_PATH 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 @@ -36,9 +33,11 @@ class ZerologonAnalyzer(Analyzer): @staticmethod def _get_relevant_credentials(config: dict): credentials_on_island = [] - credentials_on_island.extend(dpath.util.get(config["configuration"], USER_LIST_PATH)) - credentials_on_island.extend(dpath.util.get(config["configuration"], NTLM_HASH_LIST_PATH)) - credentials_on_island.extend(dpath.util.get(config["configuration"], LM_HASH_LIST_PATH)) + # TODO: Pull configured credentials and put usernames, nt and lm hashes into + # credentials_island + # credentials_on_island.extend(dpath.util.get(config["configuration"], USER_LIST_PATH)) + # credentials_on_island.extend(dpath.util.get(config["configuration"], NTLM_HASH_LIST_PATH)) + # credentials_on_island.extend(dpath.util.get(config["configuration"], LM_HASH_LIST_PATH)) return credentials_on_island def _is_all_credentials_in_list(self, all_creds: List[str]) -> bool: diff --git a/envs/monkey_zoo/blackbox/config_templates/__init__.py b/envs/monkey_zoo/blackbox/config_templates/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/envs/monkey_zoo/blackbox/config_templates/base_template.py b/envs/monkey_zoo/blackbox/config_templates/base_template.py deleted file mode 100644 index 5a1ce49a6..000000000 --- a/envs/monkey_zoo/blackbox/config_templates/base_template.py +++ /dev/null @@ -1,15 +0,0 @@ -from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate - - -# Disables a lot of config values not required for a specific feature test -class BaseTemplate(ConfigTemplate): - - config_values = { - "basic.exploiters.exploiter_classes": [], - "basic_network.scope.local_network_scan": False, - "basic_network.scope.depth": 1, - "internal.classes.finger_classes": ["HTTPFinger"], - "internal.monkey.system_info.system_info_collector_classes": [], - "monkey.post_breach.post_breach_actions": [], - "internal.general.keep_tunnel_open_time": 0, - } diff --git a/envs/monkey_zoo/blackbox/config_templates/config_template.py b/envs/monkey_zoo/blackbox/config_templates/config_template.py deleted file mode 100644 index 915a0cc78..000000000 --- a/envs/monkey_zoo/blackbox/config_templates/config_template.py +++ /dev/null @@ -1,8 +0,0 @@ -from abc import ABC, abstractmethod - - -class ConfigTemplate(ABC): - @property - @abstractmethod - def config_values(self) -> dict: - pass diff --git a/envs/monkey_zoo/blackbox/config_templates/depth_1_a.py b/envs/monkey_zoo/blackbox/config_templates/depth_1_a.py deleted file mode 100644 index b09123566..000000000 --- a/envs/monkey_zoo/blackbox/config_templates/depth_1_a.py +++ /dev/null @@ -1,42 +0,0 @@ -from copy import copy - -from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate -from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate - - -class Depth1A(ConfigTemplate): - config_values = copy(BaseTemplate.config_values) - # Tests: - # Hadoop (10.2.2.2, 10.2.2.3) - # Log4shell (10.2.3.55, 10.2.3.56, 10.2.3.49, 10.2.3.50, 10.2.3.51, 10.2.3.52) - # MSSQL (10.2.2.16) - # SMB mimikatz password stealing and brute force (10.2.2.14 and 10.2.2.15) - config_values.update( - { - "basic.exploiters.exploiter_classes": [ - "HadoopExploiter", - "Log4ShellExploiter", - "MSSQLExploiter", - "SmbExploiter", - "SSHExploiter", - ], - "basic_network.scope.subnet_scan_list": [ - "10.2.2.2", - "10.2.2.3", - "10.2.3.55", - "10.2.3.56", - "10.2.3.49", - "10.2.3.50", - "10.2.3.51", - "10.2.3.52", - "10.2.2.16", - "10.2.2.14", - "10.2.2.15", - ], - "basic.credentials.exploit_password_list": ["Ivrrw5zEzs", "Xk8VDTsC"], - "basic.credentials.exploit_user_list": ["m0nk3y"], - "monkey.system_info.system_info_collector_classes": [ - "MimikatzCollector", - ], - } - ) diff --git a/envs/monkey_zoo/blackbox/config_templates/depth_2_a.py b/envs/monkey_zoo/blackbox/config_templates/depth_2_a.py deleted file mode 100644 index d9f5168e2..000000000 --- a/envs/monkey_zoo/blackbox/config_templates/depth_2_a.py +++ /dev/null @@ -1,23 +0,0 @@ -from copy import copy - -from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate -from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate - - -class Depth2A(ConfigTemplate): - config_values = copy(BaseTemplate.config_values) - # SSH password and key brute-force, key stealing (10.2.2.11, 10.2.2.12) - config_values.update( - { - "basic.exploiters.exploiter_classes": [ - "SSHExploiter", - ], - "basic_network.scope.subnet_scan_list": [ - "10.2.2.11", - "10.2.2.12", - ], - "basic_network.scope.depth": 2, - "basic.credentials.exploit_password_list": ["^NgDvY59~8"], - "basic.credentials.exploit_user_list": ["m0nk3y"], - } - ) diff --git a/envs/monkey_zoo/blackbox/config_templates/depth_3_a.py b/envs/monkey_zoo/blackbox/config_templates/depth_3_a.py deleted file mode 100644 index 7e74f8751..000000000 --- a/envs/monkey_zoo/blackbox/config_templates/depth_3_a.py +++ /dev/null @@ -1,48 +0,0 @@ -from copy import copy - -from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate -from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate - - -class Depth3A(ConfigTemplate): - config_values = copy(BaseTemplate.config_values) - - # Tests: - # Powershell (10.2.3.45, 10.2.3.46, 10.2.3.47, 10.2.3.48) - # Tunneling (SSH brute force) (10.2.2.9, 10.2.1.10, 10.2.0.12, 10.2.0.11) - # WMI pass the hash (10.2.2.15) - config_values.update( - { - "basic.exploiters.exploiter_classes": [ - "PowerShellExploiter", - "SSHExploiter", - "WmiExploiter", - ], - "basic_network.scope.subnet_scan_list": [ - "10.2.2.9", - "10.2.3.45", - "10.2.3.46", - "10.2.3.47", - "10.2.3.48", - "10.2.1.10", - "10.2.0.12", - "10.2.0.11", - "10.2.2.15", - ], - "basic.credentials.exploit_password_list": [ - "Passw0rd!", - "3Q=(Ge(+&w]*", - "`))jU7L(w}", - "t67TC5ZDmz", - ], - "basic_network.scope.depth": 3, - "internal.general.keep_tunnel_open_time": 20, - "basic.credentials.exploit_user_list": ["m0nk3y", "m0nk3y-user"], - "internal.network.tcp_scanner.HTTP_PORTS": [], - "internal.exploits.exploit_ntlm_hash_list": [ - "d0f0132b308a0c4e5d1029cc06f48692", - "5da0889ea2081aa79f6852294cba4a5e", - "50c9987a6bf1ac59398df9f911122c9b", - ], - } - ) diff --git a/envs/monkey_zoo/blackbox/config_templates/powershell_credentials_reuse.py b/envs/monkey_zoo/blackbox/config_templates/powershell_credentials_reuse.py deleted file mode 100644 index 622cb6656..000000000 --- a/envs/monkey_zoo/blackbox/config_templates/powershell_credentials_reuse.py +++ /dev/null @@ -1,21 +0,0 @@ -from copy import copy - -from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate -from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate - - -class PowerShellCredentialsReuse(ConfigTemplate): - config_values = copy(BaseTemplate.config_values) - - config_values.update( - { - "basic.exploiters.exploiter_classes": ["PowerShellExploiter"], - "basic_network.scope.subnet_scan_list": [ - "10.2.3.46", - ], - "basic_network.scope.depth": 2, - "internal.classes.finger_classes": [], - "internal.network.tcp_scanner.HTTP_PORTS": [], - "internal.network.tcp_scanner.tcp_target_ports": [5985, 5986], - } - ) diff --git a/envs/monkey_zoo/blackbox/config_templates/smb_pth.py b/envs/monkey_zoo/blackbox/config_templates/smb_pth.py deleted file mode 100644 index cd9fed272..000000000 --- a/envs/monkey_zoo/blackbox/config_templates/smb_pth.py +++ /dev/null @@ -1,24 +0,0 @@ -from copy import copy - -from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate -from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate - - -class SmbPth(ConfigTemplate): - config_values = copy(BaseTemplate.config_values) - - config_values.update( - { - "basic.exploiters.exploiter_classes": ["SmbExploiter"], - "basic_network.scope.subnet_scan_list": ["10.2.2.15"], - "basic.credentials.exploit_password_list": ["Password1!", "Ivrrw5zEzs"], - "basic.credentials.exploit_user_list": ["Administrator", "m0nk3y", "user"], - "internal.classes.finger_classes": ["SMBFinger", "HTTPFinger"], - "internal.network.tcp_scanner.HTTP_PORTS": [], - "internal.network.tcp_scanner.tcp_target_ports": [445], - "internal.classes.exploits.exploit_ntlm_hash_list": [ - "5da0889ea2081aa79f6852294cba4a5e", - "50c9987a6bf1ac59398df9f911122c9b", - ], - } - ) diff --git a/envs/monkey_zoo/blackbox/config_templates/wmi_mimikatz.py b/envs/monkey_zoo/blackbox/config_templates/wmi_mimikatz.py deleted file mode 100644 index 430547a73..000000000 --- a/envs/monkey_zoo/blackbox/config_templates/wmi_mimikatz.py +++ /dev/null @@ -1,22 +0,0 @@ -from copy import copy - -from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate -from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate - - -class WmiMimikatz(ConfigTemplate): - config_values = copy(BaseTemplate.config_values) - - config_values.update( - { - "basic.exploiters.exploiter_classes": ["WmiExploiter"], - "basic_network.scope.subnet_scan_list": ["10.2.2.14", "10.2.2.15"], - "basic.credentials.exploit_password_list": ["Password1!", "Ivrrw5zEzs"], - "basic.credentials.exploit_user_list": ["Administrator", "m0nk3y", "user"], - "internal.network.tcp_scanner.HTTP_PORTS": [], - "internal.network.tcp_scanner.tcp_target_ports": [135], - "monkey.system_info.system_info_collector_classes": [ - "MimikatzCollector", - ], - } - ) diff --git a/envs/monkey_zoo/blackbox/config_templates/zerologon.py b/envs/monkey_zoo/blackbox/config_templates/zerologon.py deleted file mode 100644 index 0c0266857..000000000 --- a/envs/monkey_zoo/blackbox/config_templates/zerologon.py +++ /dev/null @@ -1,20 +0,0 @@ -from copy import copy - -from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate -from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate - - -class Zerologon(ConfigTemplate): - - config_values = copy(BaseTemplate.config_values) - - config_values.update( - { - "basic.exploiters.exploiter_classes": ["ZerologonExploiter", "SmbExploiter"], - "basic_network.scope.subnet_scan_list": ["10.2.2.25"], - # Empty list to make sure ZeroLogon adds "Administrator" username - "basic.credentials.exploit_user_list": [], - "internal.network.tcp_scanner.HTTP_PORTS": [], - "internal.network.tcp_scanner.tcp_target_ports": [135, 445], - } - ) diff --git a/envs/monkey_zoo/blackbox/conftest.py b/envs/monkey_zoo/blackbox/conftest.py index 82c6d3d9a..a73e6fe8b 100644 --- a/envs/monkey_zoo/blackbox/conftest.py +++ b/envs/monkey_zoo/blackbox/conftest.py @@ -14,19 +14,6 @@ def pytest_addoption(parser): default=False, help="Use for no interaction with the cloud.", ) - parser.addoption( - "--quick-performance-tests", - action="store_true", - default=False, - help="If enabled performance tests won't reset island and won't send telemetries, " - "instead will just test performance of already present island state.", - ) - parser.addoption( - "--run-performance-tests", - action="store_true", - default=False, - help="If enabled performance tests will be run.", - ) parser.addoption( "--skip-powershell-reuse", action="store_true", @@ -45,19 +32,7 @@ def no_gcp(request): return request.config.getoption("--no-gcp") -@pytest.fixture(scope="session") -def quick_performance_tests(request): - return request.config.getoption("--quick-performance-tests") - - def pytest_runtest_setup(item): - if "run_performance_tests" in item.keywords and not item.config.getoption( - "--run-performance-tests" - ): - pytest.skip( - "Skipping performance test because " "--run-performance-tests flag isn't specified." - ) - if "skip_powershell_reuse" in item.keywords and item.config.getoption( "--skip-powershell-reuse" ): diff --git a/envs/monkey_zoo/blackbox/island_client/island_config_parser.py b/envs/monkey_zoo/blackbox/island_client/island_config_parser.py deleted file mode 100644 index eda2def01..000000000 --- a/envs/monkey_zoo/blackbox/island_client/island_config_parser.py +++ /dev/null @@ -1,29 +0,0 @@ -import json - -import dpath.util -from typing_extensions import Type - -from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate -from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient - - -class IslandConfigParser: - @staticmethod - def get_raw_config( - config_template: Type[ConfigTemplate], island_client: MonkeyIslandClient - ) -> str: - response = island_client.get_config() - config = IslandConfigParser.apply_template_to_config( - config_template, response["configuration"] - ) - return json.dumps(config) - - @staticmethod - def apply_template_to_config(config_template: Type[ConfigTemplate], config: dict) -> dict: - for path, value in config_template.config_values.items(): - dpath.util.set(config, path, value, ".") - return config - - @staticmethod - def get_ips_of_targets(raw_config): - return dpath.util.get(json.loads(raw_config), "basic_network.scope.subnet_scan_list", ".") 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 cba01a488..37ab7d04a 100644 --- a/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py +++ b/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py @@ -5,7 +5,10 @@ from typing import Union from bson import json_util +from common.configuration import AgentConfiguration +from common.credentials import Credentials from envs.monkey_zoo.blackbox.island_client.monkey_island_requests import MonkeyIslandRequests +from envs.monkey_zoo.blackbox.test_configurations.test_configuration import TestConfiguration SLEEP_BETWEEN_REQUESTS_SECONDS = 0.5 MONKEY_TEST_ENDPOINT = "api/test/monkey" @@ -27,15 +30,26 @@ class MonkeyIslandClient(object): return self.requests.get("api") def get_config(self): - return json.loads(self.requests.get("api/configuration/island").content) + return json.loads(self.requests.get("api/agent-configuration").content) @avoid_race_condition - def import_config(self, config_contents): - _ = self.requests.post("api/configuration/island", data=config_contents) + def import_config(self, test_configuration: TestConfiguration): + self.requests.post_json( + "api/agent-configuration", + json=AgentConfiguration.to_mapping(test_configuration.agent_configuration), + ) + serialized_propagation_credentials = [ + Credentials.to_mapping(credentials) + for credentials in test_configuration.propagation_credentials + ] + self.requests.post_json( + "/api/propagation-credentials/configured-credentials", + json=serialized_propagation_credentials, + ) @avoid_race_condition def run_monkey_local(self): - response = self.requests.post_json("api/local-monkey", data={"action": "run"}) + response = self.requests.post_json("api/local-monkey", json={"action": "run"}) if MonkeyIslandClient.monkey_ran_successfully(response): LOGGER.info("Running the monkey.") else: @@ -49,7 +63,7 @@ class MonkeyIslandClient(object): @avoid_race_condition def kill_all_monkeys(self): response = self.requests.post_json( - "api/monkey-control/stop-all-agents", data={"kill_time": time.time()} + "api/monkey-control/stop-all-agents", json={"kill_time": time.time()} ) if response.ok: LOGGER.info("Killing all monkeys after the test.") @@ -67,10 +81,6 @@ class MonkeyIslandClient(object): LOGGER.error("Failed to reset the environment.") assert False - @avoid_race_condition - def set_scenario(self, scenario): - self.requests.post_json("api/island-mode", {"mode": scenario}) - def find_monkeys_in_db(self, query): if query is None: raise TypeError @@ -110,13 +120,3 @@ 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.get("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 3f8efb227..ff3b32e92 100644 --- a/envs/monkey_zoo/blackbox/island_client/monkey_island_requests.py +++ b/envs/monkey_zoo/blackbox/island_client/monkey_island_requests.py @@ -1,12 +1,9 @@ import functools import logging -from datetime import timedelta from typing import Dict import requests -from envs.monkey_zoo.blackbox.island_client.supported_request_method import SupportedRequestMethod - ISLAND_USERNAME = "test" ISLAND_PASSWORD = "test" LOGGER = logging.getLogger(__name__) @@ -25,28 +22,6 @@ class MonkeyIslandRequests(object): def __init__(self, server_address): self.addr = "https://{IP}/".format(IP=server_address) self.token = self.try_get_jwt_from_server() - self.supported_request_methods = { - SupportedRequestMethod.GET: self.get, - SupportedRequestMethod.POST: self.post, - SupportedRequestMethod.PATCH: self.patch, - SupportedRequestMethod.DELETE: self.delete, - } - - def get_request_time(self, url, method: SupportedRequestMethod, data=None): - response = self.send_request_by_method(url, method, data) - 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 - - def send_request_by_method(self, url, method=SupportedRequestMethod.GET, data=None): - if data: - return self.supported_request_methods[method](url, data) - else: - return self.supported_request_methods[method](url) def try_get_jwt_from_server(self): try: @@ -108,9 +83,9 @@ class MonkeyIslandRequests(object): ) @_Decorators.refresh_jwt_token - def post_json(self, url, data: Dict): + def post_json(self, url, json: Dict): return requests.post( # noqa: DUO123 - self.addr + url, json=data, headers=self.get_jwt_header(), verify=False + self.addr + url, json=json, headers=self.get_jwt_header(), verify=False ) @_Decorators.refresh_jwt_token diff --git a/envs/monkey_zoo/blackbox/island_client/supported_request_method.py b/envs/monkey_zoo/blackbox/island_client/supported_request_method.py deleted file mode 100644 index 60cb0877a..000000000 --- a/envs/monkey_zoo/blackbox/island_client/supported_request_method.py +++ /dev/null @@ -1,8 +0,0 @@ -from enum import Enum - - -class SupportedRequestMethod(Enum): - GET = "GET" - POST = "POST" - PATCH = "PATCH" - DELETE = "DELETE" diff --git a/envs/monkey_zoo/blackbox/island_client/test_configuration_parser.py b/envs/monkey_zoo/blackbox/island_client/test_configuration_parser.py new file mode 100644 index 000000000..9fcb6c8a6 --- /dev/null +++ b/envs/monkey_zoo/blackbox/island_client/test_configuration_parser.py @@ -0,0 +1,7 @@ +from typing import Iterable + +from envs.monkey_zoo.blackbox.test_configurations.test_configuration import TestConfiguration + + +def get_target_ips(test_configuration: TestConfiguration) -> Iterable[str]: + return test_configuration.agent_configuration.propagation.network_scan.targets.subnets diff --git a/envs/monkey_zoo/blackbox/test_blackbox.py b/envs/monkey_zoo/blackbox/test_blackbox.py index 6cafe1264..961d094fb 100644 --- a/envs/monkey_zoo/blackbox/test_blackbox.py +++ b/envs/monkey_zoo/blackbox/test_blackbox.py @@ -3,31 +3,29 @@ import os from time import sleep import pytest -from typing_extensions import Type from envs.monkey_zoo.blackbox.analyzers.communication_analyzer import CommunicationAnalyzer from envs.monkey_zoo.blackbox.analyzers.zerologon_analyzer import ZerologonAnalyzer -from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate -from envs.monkey_zoo.blackbox.config_templates.depth_1_a import Depth1A -from envs.monkey_zoo.blackbox.config_templates.depth_2_a import Depth2A -from envs.monkey_zoo.blackbox.config_templates.depth_3_a import Depth3A -from envs.monkey_zoo.blackbox.config_templates.powershell_credentials_reuse import ( - PowerShellCredentialsReuse, -) -from envs.monkey_zoo.blackbox.config_templates.smb_pth import SmbPth -from envs.monkey_zoo.blackbox.config_templates.wmi_mimikatz import WmiMimikatz -from envs.monkey_zoo.blackbox.config_templates.zerologon import Zerologon from envs.monkey_zoo.blackbox.gcp_test_machine_list import GCP_TEST_MACHINE_LIST -from envs.monkey_zoo.blackbox.island_client.island_config_parser import IslandConfigParser from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient +from envs.monkey_zoo.blackbox.island_client.test_configuration_parser import get_target_ips from envs.monkey_zoo.blackbox.log_handlers.test_logs_handler import TestLogsHandler +from envs.monkey_zoo.blackbox.test_configurations import ( + depth_1_a_test_configuration, + depth_2_a_test_configuration, + depth_3_a_test_configuration, + powershell_credentials_reuse_test_configuration, + smb_pth_test_configuration, + wmi_mimikatz_test_configuration, + zerologon_test_configuration, +) +from envs.monkey_zoo.blackbox.test_configurations.test_configuration import TestConfiguration from envs.monkey_zoo.blackbox.tests.exploitation import ExploitationTest from envs.monkey_zoo.blackbox.utils.gcp_machine_handlers import ( initialize_gcp_client, start_machines, stop_machines, ) -from monkey_island.cc.services.mode.mode_enum import IslandModeEnum DEFAULT_TIMEOUT_SECONDS = 2 * 60 + 30 MACHINE_BOOTUP_WAIT_SECONDS = 30 @@ -64,7 +62,7 @@ def wait_machine_bootup(): @pytest.fixture(scope="class") -def island_client(island, quick_performance_tests): +def island_client(island): client_established = False try: island_client_object = MonkeyIslandClient(island) @@ -74,9 +72,6 @@ def island_client(island, quick_performance_tests): finally: if not client_established: pytest.exit("BB tests couldn't establish communication to the island.") - if not quick_performance_tests: - island_client_object.reset_env() - island_client_object.set_scenario(IslandModeEnum.ADVANCED.value) yield island_client_object @@ -86,13 +81,13 @@ class TestMonkeyBlackbox: @staticmethod def run_exploitation_test( island_client: MonkeyIslandClient, - config_template: Type[ConfigTemplate], + test_configuration: TestConfiguration, test_name: str, timeout_in_seconds=DEFAULT_TIMEOUT_SECONDS, ): - raw_config = IslandConfigParser.get_raw_config(config_template, island_client) analyzer = CommunicationAnalyzer( - island_client, IslandConfigParser.get_ips_of_targets(raw_config) + island_client, + get_target_ips(test_configuration), ) log_handler = TestLogsHandler( test_name, island_client, TestMonkeyBlackbox.get_log_dir_path() @@ -100,7 +95,7 @@ class TestMonkeyBlackbox: ExploitationTest( name=test_name, island_client=island_client, - raw_config=raw_config, + test_configuration=test_configuration, analyzers=[analyzer], timeout=timeout_in_seconds, log_handler=log_handler, @@ -113,20 +108,26 @@ class TestMonkeyBlackbox: # If test_depth_1_a() is run first, some test will fail because machines are not yet fully # booted. Running test_depth_2_a() first gives slow VMs extra time to boot. def test_depth_2_a(self, island_client): - TestMonkeyBlackbox.run_exploitation_test(island_client, Depth2A, "Depth2A test suite") + TestMonkeyBlackbox.run_exploitation_test( + island_client, depth_2_a_test_configuration, "Depth2A test suite" + ) def test_depth_1_a(self, island_client): - TestMonkeyBlackbox.run_exploitation_test(island_client, Depth1A, "Depth1A test suite") + TestMonkeyBlackbox.run_exploitation_test( + island_client, depth_1_a_test_configuration, "Depth1A test suite" + ) def test_depth_3_a(self, island_client): - TestMonkeyBlackbox.run_exploitation_test(island_client, Depth3A, "Depth3A test suite") + TestMonkeyBlackbox.run_exploitation_test( + island_client, depth_3_a_test_configuration, "Depth3A test suite" + ) # Not grouped because can only be ran on windows @pytest.mark.skip_powershell_reuse def test_powershell_exploiter_credentials_reuse(self, island_client): TestMonkeyBlackbox.run_exploitation_test( island_client, - PowerShellCredentialsReuse, + powershell_credentials_reuse_test_configuration, "PowerShell_Remoting_exploiter_credentials_reuse", ) @@ -138,10 +139,10 @@ class TestMonkeyBlackbox: "aad3b435b51404eeaad3b435b51404ee", "2864b62ea4496934a5d6e86f50b834a5", ] - raw_config = IslandConfigParser.get_raw_config(Zerologon, island_client) zero_logon_analyzer = ZerologonAnalyzer(island_client, expected_creds) communication_analyzer = CommunicationAnalyzer( - island_client, IslandConfigParser.get_ips_of_targets(raw_config) + island_client, + get_target_ips(zerologon_test_configuration), ) log_handler = TestLogsHandler( test_name, island_client, TestMonkeyBlackbox.get_log_dir_path() @@ -149,7 +150,7 @@ class TestMonkeyBlackbox: ExploitationTest( name=test_name, island_client=island_client, - raw_config=raw_config, + test_configuration=zerologon_test_configuration, analyzers=[zero_logon_analyzer, communication_analyzer], timeout=DEFAULT_TIMEOUT_SECONDS + 30, log_handler=log_handler, @@ -159,9 +160,11 @@ class TestMonkeyBlackbox: # Consider grouping when more depth 1 exploiters collide with group depth_1_a def test_wmi_and_mimikatz_exploiters(self, island_client): TestMonkeyBlackbox.run_exploitation_test( - island_client, WmiMimikatz, "WMI_exploiter,_mimikatz" + island_client, wmi_mimikatz_test_configuration, "WMI_exploiter,_mimikatz" ) # Not grouped because it's depth 1 but conflicts with SMB exploiter in group depth_1_a def test_smb_pth(self, island_client): - TestMonkeyBlackbox.run_exploitation_test(island_client, SmbPth, "SMB_PTH") + TestMonkeyBlackbox.run_exploitation_test( + island_client, smb_pth_test_configuration, "SMB_PTH" + ) diff --git a/envs/monkey_zoo/blackbox/test_configurations/__init__.py b/envs/monkey_zoo/blackbox/test_configurations/__init__.py index 9aa48000e..6a1634727 100644 --- a/envs/monkey_zoo/blackbox/test_configurations/__init__.py +++ b/envs/monkey_zoo/blackbox/test_configurations/__init__.py @@ -1,8 +1,8 @@ from .test_configuration import TestConfiguration -from .zerologon import zerologon_test_configuration from .depth_1_a import depth_1_a_test_configuration from .depth_2_a import depth_2_a_test_configuration from .depth_3_a import depth_3_a_test_configuration from .powershell_credentials_reuse import powershell_credentials_reuse_test_configuration from .smb_pth import smb_pth_test_configuration from .wmi_mimikatz import wmi_mimikatz_test_configuration +from .zerologon import zerologon_test_configuration diff --git a/envs/monkey_zoo/blackbox/tests/exploitation.py b/envs/monkey_zoo/blackbox/tests/exploitation.py index f439e11db..b4cf40eba 100644 --- a/envs/monkey_zoo/blackbox/tests/exploitation.py +++ b/envs/monkey_zoo/blackbox/tests/exploitation.py @@ -1,7 +1,7 @@ import logging from time import sleep -from envs.monkey_zoo.blackbox.island_client.island_config_parser import IslandConfigParser +from envs.monkey_zoo.blackbox.island_client.test_configuration_parser import get_target_ips from envs.monkey_zoo.blackbox.tests.basic_test import BasicTest from envs.monkey_zoo.blackbox.utils.test_timer import TestTimer @@ -13,16 +13,16 @@ LOGGER = logging.getLogger(__name__) class ExploitationTest(BasicTest): - def __init__(self, name, island_client, raw_config, analyzers, timeout, log_handler): + def __init__(self, name, island_client, test_configuration, analyzers, timeout, log_handler): self.name = name self.island_client = island_client - self.raw_config = raw_config + self.test_configuration = test_configuration self.analyzers = analyzers self.timeout = timeout self.log_handler = log_handler def run(self): - self.island_client.import_config(self.raw_config) + self.island_client.import_config(self.test_configuration) self.print_test_starting_info() try: self.island_client.run_monkey_local() @@ -36,7 +36,7 @@ class ExploitationTest(BasicTest): def print_test_starting_info(self): LOGGER.info("Started {} test".format(self.name)) - machine_list = ", ".join(IslandConfigParser.get_ips_of_targets(self.raw_config)) + machine_list = ", ".join(get_target_ips(self.test_configuration)) LOGGER.info(f"Machines participating in test: {machine_list}") print("") diff --git a/envs/monkey_zoo/blackbox/tests/performance/__init__.py b/envs/monkey_zoo/blackbox/tests/performance/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/envs/monkey_zoo/blackbox/tests/performance/endpoint_performance_test.py b/envs/monkey_zoo/blackbox/tests/performance/endpoint_performance_test.py deleted file mode 100644 index 1e2345ecf..000000000 --- a/envs/monkey_zoo/blackbox/tests/performance/endpoint_performance_test.py +++ /dev/null @@ -1,28 +0,0 @@ -import logging - -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.island_client.supported_request_method import SupportedRequestMethod -from envs.monkey_zoo.blackbox.tests.basic_test import BasicTest -from envs.monkey_zoo.blackbox.tests.performance.performance_test_config import PerformanceTestConfig - -LOGGER = logging.getLogger(__name__) - - -class EndpointPerformanceTest(BasicTest): - def __init__(self, name, test_config: PerformanceTestConfig, island_client: MonkeyIslandClient): - self.name = name - self.test_config = test_config - self.island_client = island_client - - def run(self) -> bool: - # Collect timings for all pages - endpoint_timings = {} - for endpoint in self.test_config.endpoints_to_test: - self.island_client.clear_caches() - endpoint_timings[endpoint] = self.island_client.requests.get_request_time( - endpoint, SupportedRequestMethod.GET - ) - analyzer = PerformanceAnalyzer(self.test_config, endpoint_timings) - - return analyzer.analyze_test_results() diff --git a/envs/monkey_zoo/blackbox/tests/performance/map_generation.py b/envs/monkey_zoo/blackbox/tests/performance/map_generation.py deleted file mode 100644 index f925f031d..000000000 --- a/envs/monkey_zoo/blackbox/tests/performance/map_generation.py +++ /dev/null @@ -1,40 +0,0 @@ -from datetime import timedelta - -from envs.monkey_zoo.blackbox.tests.exploitation import ExploitationTest -from envs.monkey_zoo.blackbox.tests.performance.performance_test import PerformanceTest -from envs.monkey_zoo.blackbox.tests.performance.performance_test_config import PerformanceTestConfig -from envs.monkey_zoo.blackbox.tests.performance.performance_test_workflow import ( - PerformanceTestWorkflow, -) - -MAX_ALLOWED_SINGLE_PAGE_TIME = timedelta(seconds=2) -MAX_ALLOWED_TOTAL_TIME = timedelta(seconds=5) - -MAP_RESOURCES = [ - "api/netmap", -] - - -class MapGenerationTest(PerformanceTest): - - TEST_NAME = "Map generation performance test" - - def __init__( - self, island_client, raw_config, analyzers, timeout, log_handler, break_on_timeout - ): - self.island_client = island_client - exploitation_test = ExploitationTest( - MapGenerationTest.TEST_NAME, island_client, raw_config, analyzers, timeout, log_handler - ) - performance_config = PerformanceTestConfig( - max_allowed_single_page_time=MAX_ALLOWED_SINGLE_PAGE_TIME, - max_allowed_total_time=MAX_ALLOWED_TOTAL_TIME, - endpoints_to_test=MAP_RESOURCES, - break_on_timeout=break_on_timeout, - ) - self.performance_test_workflow = PerformanceTestWorkflow( - MapGenerationTest.TEST_NAME, exploitation_test, performance_config - ) - - def run(self): - self.performance_test_workflow.run() diff --git a/envs/monkey_zoo/blackbox/tests/performance/map_generation_from_telemetries.py b/envs/monkey_zoo/blackbox/tests/performance/map_generation_from_telemetries.py deleted file mode 100644 index 8713d3c0f..000000000 --- a/envs/monkey_zoo/blackbox/tests/performance/map_generation_from_telemetries.py +++ /dev/null @@ -1,37 +0,0 @@ -from datetime import timedelta - -from envs.monkey_zoo.blackbox.tests.performance.performance_test import PerformanceTest -from envs.monkey_zoo.blackbox.tests.performance.performance_test_config import PerformanceTestConfig -from envs.monkey_zoo.blackbox.tests.performance.telemetry_performance_test_workflow import ( - TelemetryPerformanceTestWorkflow, -) - -MAX_ALLOWED_SINGLE_PAGE_TIME = timedelta(seconds=2) -MAX_ALLOWED_TOTAL_TIME = timedelta(seconds=5) - -MAP_RESOURCES = [ - "api/netmap", -] - - -class MapGenerationFromTelemetryTest(PerformanceTest): - - TEST_NAME = "Map generation from fake telemetries test" - - def __init__(self, island_client, quick_performance_test: bool, break_on_timeout=False): - self.island_client = island_client - performance_config = PerformanceTestConfig( - max_allowed_single_page_time=MAX_ALLOWED_SINGLE_PAGE_TIME, - max_allowed_total_time=MAX_ALLOWED_TOTAL_TIME, - endpoints_to_test=MAP_RESOURCES, - break_on_timeout=break_on_timeout, - ) - self.performance_test_workflow = TelemetryPerformanceTestWorkflow( - MapGenerationFromTelemetryTest.TEST_NAME, - self.island_client, - performance_config, - quick_performance_test, - ) - - def run(self): - self.performance_test_workflow.run() diff --git a/envs/monkey_zoo/blackbox/tests/performance/performance_test.py b/envs/monkey_zoo/blackbox/tests/performance/performance_test.py deleted file mode 100644 index de5d49945..000000000 --- a/envs/monkey_zoo/blackbox/tests/performance/performance_test.py +++ /dev/null @@ -1,16 +0,0 @@ -from abc import ABCMeta, abstractmethod - -from envs.monkey_zoo.blackbox.tests.basic_test import BasicTest - - -class PerformanceTest(BasicTest, metaclass=ABCMeta): - @abstractmethod - def __init__( - self, island_client, raw_config, analyzers, timeout, log_handler, break_on_timeout - ): - pass - - @property - @abstractmethod - def TEST_NAME(self): - pass diff --git a/envs/monkey_zoo/blackbox/tests/performance/performance_test_config.py b/envs/monkey_zoo/blackbox/tests/performance/performance_test_config.py deleted file mode 100644 index cc45093c0..000000000 --- a/envs/monkey_zoo/blackbox/tests/performance/performance_test_config.py +++ /dev/null @@ -1,16 +0,0 @@ -from datetime import timedelta -from typing import List - - -class PerformanceTestConfig: - def __init__( - self, - max_allowed_single_page_time: timedelta, - max_allowed_total_time: timedelta, - endpoints_to_test: List[str] = None, - break_on_timeout=False, - ): - self.max_allowed_single_page_time = max_allowed_single_page_time - self.max_allowed_total_time = max_allowed_total_time - self.endpoints_to_test = endpoints_to_test - self.break_on_timeout = break_on_timeout diff --git a/envs/monkey_zoo/blackbox/tests/performance/performance_test_workflow.py b/envs/monkey_zoo/blackbox/tests/performance/performance_test_workflow.py deleted file mode 100644 index de63ed899..000000000 --- a/envs/monkey_zoo/blackbox/tests/performance/performance_test_workflow.py +++ /dev/null @@ -1,40 +0,0 @@ -from envs.monkey_zoo.blackbox.tests.basic_test import BasicTest -from envs.monkey_zoo.blackbox.tests.exploitation import ExploitationTest -from envs.monkey_zoo.blackbox.tests.performance.endpoint_performance_test import ( - EndpointPerformanceTest, -) -from envs.monkey_zoo.blackbox.tests.performance.performance_test_config import PerformanceTestConfig - - -class PerformanceTestWorkflow(BasicTest): - def __init__( - self, name, exploitation_test: ExploitationTest, performance_config: PerformanceTestConfig - ): - self.name = name - self.exploitation_test = exploitation_test - self.island_client = exploitation_test.island_client - self.raw_config = exploitation_test.raw_config - self.performance_config = performance_config - - def run(self): - self.island_client.import_config(self.raw_config) - self.exploitation_test.print_test_starting_info() - try: - self.island_client.run_monkey_local() - self.exploitation_test.test_until_timeout() - finally: - self.island_client.kill_all_monkeys() - self.exploitation_test.wait_until_monkeys_die() - self.exploitation_test.wait_for_monkey_process_to_finish() - if not self.island_client.is_all_monkeys_dead(): - raise RuntimeError("Can't test report times since not all Monkeys have died.") - performance_test = EndpointPerformanceTest( - self.name, self.performance_config, self.island_client - ) - try: - if not self.island_client.is_all_monkeys_dead(): - raise RuntimeError("Can't test report times since not all Monkeys have died.") - assert performance_test.run() - finally: - self.exploitation_test.parse_logs() - self.island_client.reset_env() diff --git a/envs/monkey_zoo/blackbox/tests/performance/report_generation.py b/envs/monkey_zoo/blackbox/tests/performance/report_generation.py deleted file mode 100644 index c7efc6057..000000000 --- a/envs/monkey_zoo/blackbox/tests/performance/report_generation.py +++ /dev/null @@ -1,48 +0,0 @@ -from datetime import timedelta - -from envs.monkey_zoo.blackbox.tests.exploitation import ExploitationTest -from envs.monkey_zoo.blackbox.tests.performance.performance_test import PerformanceTest -from envs.monkey_zoo.blackbox.tests.performance.performance_test_config import PerformanceTestConfig -from envs.monkey_zoo.blackbox.tests.performance.performance_test_workflow import ( - PerformanceTestWorkflow, -) - -MAX_ALLOWED_SINGLE_PAGE_TIME = timedelta(seconds=2) -MAX_ALLOWED_TOTAL_TIME = timedelta(seconds=5) - -REPORT_RESOURCES = [ - "api/report/security", - "api/attack/report", - "api/report/zero_trust/findings", - "api/report/zero_trust/principles", - "api/report/zero_trust/pillars", -] - - -class ReportGenerationTest(PerformanceTest): - TEST_NAME = "Report generation performance test" - - def __init__( - self, island_client, raw_config, analyzers, timeout, log_handler, break_on_timeout - ): - self.island_client = island_client - exploitation_test = ExploitationTest( - ReportGenerationTest.TEST_NAME, - island_client, - raw_config, - analyzers, - timeout, - log_handler, - ) - performance_config = PerformanceTestConfig( - max_allowed_single_page_time=MAX_ALLOWED_SINGLE_PAGE_TIME, - max_allowed_total_time=MAX_ALLOWED_TOTAL_TIME, - endpoints_to_test=REPORT_RESOURCES, - break_on_timeout=break_on_timeout, - ) - self.performance_test_workflow = PerformanceTestWorkflow( - ReportGenerationTest.TEST_NAME, exploitation_test, performance_config - ) - - def run(self): - self.performance_test_workflow.run() diff --git a/envs/monkey_zoo/blackbox/tests/performance/report_generation_from_telemetries.py b/envs/monkey_zoo/blackbox/tests/performance/report_generation_from_telemetries.py deleted file mode 100644 index 59c7e1848..000000000 --- a/envs/monkey_zoo/blackbox/tests/performance/report_generation_from_telemetries.py +++ /dev/null @@ -1,41 +0,0 @@ -from datetime import timedelta - -from envs.monkey_zoo.blackbox.tests.performance.performance_test import PerformanceTest -from envs.monkey_zoo.blackbox.tests.performance.performance_test_config import PerformanceTestConfig -from envs.monkey_zoo.blackbox.tests.performance.telemetry_performance_test_workflow import ( - TelemetryPerformanceTestWorkflow, -) - -MAX_ALLOWED_SINGLE_PAGE_TIME = timedelta(seconds=2) -MAX_ALLOWED_TOTAL_TIME = timedelta(seconds=5) - -REPORT_RESOURCES = [ - "api/report/security", - "api/attack/report", - "api/report/zero_trust/findings", - "api/report/zero_trust/principles", - "api/report/zero_trust/pillars", -] - - -class ReportGenerationFromTelemetryTest(PerformanceTest): - - TEST_NAME = "Map generation from fake telemetries test" - - def __init__(self, island_client, quick_performance_test, break_on_timeout=False): - self.island_client = island_client - performance_config = PerformanceTestConfig( - max_allowed_single_page_time=MAX_ALLOWED_SINGLE_PAGE_TIME, - max_allowed_total_time=MAX_ALLOWED_TOTAL_TIME, - endpoints_to_test=REPORT_RESOURCES, - break_on_timeout=break_on_timeout, - ) - self.performance_test_workflow = TelemetryPerformanceTestWorkflow( - ReportGenerationFromTelemetryTest.TEST_NAME, - self.island_client, - performance_config, - quick_performance_test, - ) - - def run(self): - self.performance_test_workflow.run() diff --git a/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/__init__.py b/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_file_parser.py b/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_file_parser.py deleted file mode 100644 index c0eeafd5c..000000000 --- a/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_file_parser.py +++ /dev/null @@ -1,51 +0,0 @@ -import json -import logging -from os import listdir, path -from typing import Dict, List - -from tqdm import tqdm - -TELEM_DIR_PATH = "../envs/monkey_zoo/blackbox/tests/performance/telemetry_sample" -MAX_SAME_TYPE_TELEM_FILES = 10000 -LOGGER = logging.getLogger(__name__) - - -class SampleFileParser: - @staticmethod - def save_teletries_to_files(telems: List[Dict]): - for telem in tqdm(telems, desc="Telemetries saved to files", position=3): - SampleFileParser.save_telemetry_to_file(telem) - - @staticmethod - def save_telemetry_to_file(telem: Dict): - telem_filename = telem["name"] + telem["method"] - for i in range(MAX_SAME_TYPE_TELEM_FILES): - if not path.exists(path.join(TELEM_DIR_PATH, (str(i) + telem_filename))): - telem_filename = str(i) + telem_filename - break - with open(path.join(TELEM_DIR_PATH, telem_filename), "w") as file: - file.write(json.dumps(telem)) - - @staticmethod - def read_telem_files() -> List[str]: - telems = [] - try: - file_paths = [ - path.join(TELEM_DIR_PATH, f) - for f in listdir(TELEM_DIR_PATH) - if path.isfile(path.join(TELEM_DIR_PATH, f)) - ] - except FileNotFoundError: - raise FileNotFoundError( - "Telemetries to send not found. " - "Refer to readme to figure out how to generate telemetries and where to put them." - ) - for file_path in file_paths: - with open(file_path, "r") as telem_file: - telem_string = "".join(telem_file.readlines()).replace("\n", "") - telems.append(telem_string) - return telems - - @staticmethod - def get_all_telemetries() -> List[Dict]: - return [json.loads(t) for t in SampleFileParser.read_telem_files()] diff --git a/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/__init__.py b/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/fake_ip_generator.py b/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/fake_ip_generator.py deleted file mode 100644 index 70bb69de4..000000000 --- a/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/fake_ip_generator.py +++ /dev/null @@ -1,25 +0,0 @@ -from typing import List - - -class FakeIpGenerator: - def __init__(self): - self.fake_ip_parts = [1, 1, 1, 1] - - def generate_fake_ips_for_real_ips(self, real_ips: List[str]) -> List[str]: - fake_ips = [] - for i in range(len(real_ips)): - fake_ips.append(".".join(str(part) for part in self.fake_ip_parts)) - self.increment_ip() - return fake_ips - - def increment_ip(self): - self.fake_ip_parts[3] += 1 - self.try_fix_ip_range() - - def try_fix_ip_range(self): - for i in range(len(self.fake_ip_parts)): - if self.fake_ip_parts[i] > 256: - if i - 1 < 0: - raise Exception("Fake IP's out of range.") - self.fake_ip_parts[i - 1] += 1 - self.fake_ip_parts[i] = 1 diff --git a/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/fake_monkey.py b/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/fake_monkey.py deleted file mode 100644 index 37245cefc..000000000 --- a/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/fake_monkey.py +++ /dev/null @@ -1,19 +0,0 @@ -import random - -from envs.monkey_zoo.blackbox.tests.performance.telem_sample_parsing.sample_multiplier.fake_ip_generator import ( # noqa: E501 - FakeIpGenerator, -) - - -class FakeMonkey: - def __init__(self, ips, guid, fake_ip_generator: FakeIpGenerator, on_island=False): - self.original_ips = ips - self.original_guid = guid - self.fake_ip_generator = fake_ip_generator - self.on_island = on_island - self.fake_guid = str(random.randint(1000000000000, 9999999999999)) # noqa: DUO102 - self.fake_ips = fake_ip_generator.generate_fake_ips_for_real_ips(ips) - - def change_fake_data(self): - self.fake_ips = self.fake_ip_generator.generate_fake_ips_for_real_ips(self.original_ips) - self.fake_guid = str(random.randint(1000000000000, 9999999999999)) # noqa: DUO102 diff --git a/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/sample_multiplier.py b/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/sample_multiplier.py deleted file mode 100644 index 8ec9bb346..000000000 --- a/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/sample_multiplier.py +++ /dev/null @@ -1,107 +0,0 @@ -import copy -import json -import logging -import sys -from typing import Dict, List - -from tqdm import tqdm - -from envs.monkey_zoo.blackbox.tests.performance.telem_sample_parsing.sample_file_parser import ( - SampleFileParser, -) -from envs.monkey_zoo.blackbox.tests.performance.telem_sample_parsing.sample_multiplier.fake_ip_generator import ( # noqa: E501 - FakeIpGenerator, -) -from envs.monkey_zoo.blackbox.tests.performance.telem_sample_parsing.sample_multiplier.fake_monkey import ( # noqa: E501 - FakeMonkey, -) - -LOGGER = logging.getLogger(__name__) - - -class SampleMultiplier: - def __init__(self, multiplier: int): - self.multiplier = multiplier - self.fake_ip_generator = FakeIpGenerator() - - def multiply_telems(self): - telems = SampleFileParser.get_all_telemetries() - telem_contents = [json.loads(telem["content"]) for telem in telems] - monkeys = self.get_monkeys_from_telems(telem_contents) - for i in tqdm(range(self.multiplier), desc="Batch of fabricated telemetries", position=1): - for monkey in monkeys: - monkey.change_fake_data() - fake_telem_batch = copy.deepcopy(telems) - SampleMultiplier.fabricate_monkeys_in_telems(fake_telem_batch, monkeys) - SampleMultiplier.offset_telem_times(iteration=i, telems=fake_telem_batch) - SampleFileParser.save_teletries_to_files(fake_telem_batch) - LOGGER.info("") - - @staticmethod - def fabricate_monkeys_in_telems(telems: List[Dict], monkeys: List[FakeMonkey]): - for telem in tqdm(telems, desc="Telemetries fabricated", position=2): - for monkey in monkeys: - if monkey.on_island: - continue - if ( - monkey.original_guid in telem["content"] - or monkey.original_guid in telem["endpoint"] - ) and not monkey.on_island: - telem["content"] = telem["content"].replace( - monkey.original_guid, monkey.fake_guid - ) - telem["endpoint"] = telem["endpoint"].replace( - monkey.original_guid, monkey.fake_guid - ) - for i in range(len(monkey.original_ips)): - telem["content"] = telem["content"].replace( - monkey.original_ips[i], monkey.fake_ips[i] - ) - - @staticmethod - def offset_telem_times(iteration: int, telems: List[Dict]): - for telem in telems: - telem["time"]["$date"] += iteration * 1000 - - def get_monkeys_from_telems(self, telems: List[Dict]): - island_ips = SampleMultiplier.get_island_ips_from_telems(telems) - monkeys = [] - for telem in [ - telem - for telem in telems - if "telem_category" in telem and telem["telem_category"] == "system_info" - ]: - if "network_info" not in telem["data"]: - continue - guid = telem["monkey_guid"] - monkey_present = [monkey for monkey in monkeys if monkey.original_guid == guid] - if not monkey_present: - ips = [net_info["addr"] for net_info in telem["data"]["network_info"]["networks"]] - if set(island_ips).intersection(ips): - on_island = True - else: - on_island = False - - monkeys.append( - FakeMonkey( - ips=ips, - guid=guid, - fake_ip_generator=self.fake_ip_generator, - on_island=on_island, - ) - ) - return monkeys - - @staticmethod - def get_island_ips_from_telems(telems: List[Dict]) -> List[str]: - island_ips = [] - for telem in telems: - if "config" in telem: - island_ips = telem["config"]["command_servers"] - for i in range(len(island_ips)): - island_ips[i] = island_ips[i].replace(":5000", "") - return island_ips - - -if __name__ == "__main__": - SampleMultiplier(multiplier=int(sys.argv[1])).multiply_telems() diff --git a/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/test_fake_ip_generator.py b/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/test_fake_ip_generator.py deleted file mode 100644 index 55662b307..000000000 --- a/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/test_fake_ip_generator.py +++ /dev/null @@ -1,21 +0,0 @@ -from unittest import TestCase - -from envs.monkey_zoo.blackbox.tests.performance.telem_sample_parsing.sample_multiplier.fake_ip_generator import ( # noqa: E501 - FakeIpGenerator, -) - - -class TestFakeIpGenerator(TestCase): - def test_fake_ip_generation(self): - fake_ip_gen = FakeIpGenerator() - self.assertListEqual([1, 1, 1, 1], fake_ip_gen.fake_ip_parts) - for i in range(256): - fake_ip_gen.generate_fake_ips_for_real_ips(["1.1.1.1"]) - self.assertListEqual(["1.1.2.1"], fake_ip_gen.generate_fake_ips_for_real_ips(["1.1.1.1"])) - fake_ip_gen.fake_ip_parts = [256, 256, 255, 256] - self.assertListEqual( - ["256.256.255.256", "256.256.256.1"], - fake_ip_gen.generate_fake_ips_for_real_ips(["1.1.1.1", "1.1.1.2"]), - ) - fake_ip_gen.fake_ip_parts = [256, 256, 256, 256] - self.assertRaises(Exception, fake_ip_gen.generate_fake_ips_for_real_ips(["1.1.1.1"])) diff --git a/envs/monkey_zoo/blackbox/tests/performance/telemetry_performance_test.py b/envs/monkey_zoo/blackbox/tests/performance/telemetry_performance_test.py deleted file mode 100644 index 31179d713..000000000 --- a/envs/monkey_zoo/blackbox/tests/performance/telemetry_performance_test.py +++ /dev/null @@ -1,66 +0,0 @@ -import json -import logging -from datetime import timedelta - -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.island_client.supported_request_method import SupportedRequestMethod -from envs.monkey_zoo.blackbox.tests.performance.performance_test_config import PerformanceTestConfig -from envs.monkey_zoo.blackbox.tests.performance.telem_sample_parsing.sample_file_parser import ( - SampleFileParser, -) - -LOGGER = logging.getLogger(__name__) - -MAX_ALLOWED_SINGLE_TELEM_PARSE_TIME = timedelta(seconds=2) -MAX_ALLOWED_TOTAL_TIME = timedelta(seconds=60) - - -class TelemetryPerformanceTest: - def __init__(self, island_client: MonkeyIslandClient, quick_performance_test: bool): - self.island_client = island_client - self.quick_performance_test = quick_performance_test - - def test_telemetry_performance(self): - LOGGER.info("Starting telemetry performance test.") - try: - all_telemetries = SampleFileParser.get_all_telemetries() - except FileNotFoundError: - raise FileNotFoundError( - "Telemetries to send not found. " - "Refer to readme to figure out how to generate telemetries and where to put them." - ) - LOGGER.info("Telemetries imported successfully.") - all_telemetries.sort(key=lambda telem: telem["time"]["$date"]) - telemetry_parse_times = {} - for i in range(len(all_telemetries)): - telemetry_endpoint = TelemetryPerformanceTest.get_verbose_telemetry_endpoint( - all_telemetries[i] - ) - telemetry_parse_times[telemetry_endpoint] = self.get_telemetry_time(all_telemetries[i]) - LOGGER.info(f"Telemetry Nr.{i} sent out of {len(all_telemetries)} total.") - test_config = PerformanceTestConfig( - MAX_ALLOWED_SINGLE_TELEM_PARSE_TIME, MAX_ALLOWED_TOTAL_TIME - ) - PerformanceAnalyzer(test_config, telemetry_parse_times).analyze_test_results() - if not self.quick_performance_test: - self.island_client.reset_env() - - def get_telemetry_time(self, telemetry): - content = telemetry["content"] - url = telemetry["endpoint"] - method = SupportedRequestMethod.__getattr__(telemetry["method"]) - - return self.island_client.requests.get_request_time(url=url, method=method, data=content) - - @staticmethod - def get_verbose_telemetry_endpoint(telemetry): - telem_category = "" - if "telem_category" in telemetry["content"]: - telem_category = ( - "_" - + json.loads(telemetry["content"])["telem_category"] - + "_" - + telemetry["_id"]["$oid"] - ) - return telemetry["endpoint"] + telem_category diff --git a/envs/monkey_zoo/blackbox/tests/performance/telemetry_performance_test_workflow.py b/envs/monkey_zoo/blackbox/tests/performance/telemetry_performance_test_workflow.py deleted file mode 100644 index b492bf9e6..000000000 --- a/envs/monkey_zoo/blackbox/tests/performance/telemetry_performance_test_workflow.py +++ /dev/null @@ -1,34 +0,0 @@ -from envs.monkey_zoo.blackbox.tests.basic_test import BasicTest -from envs.monkey_zoo.blackbox.tests.performance.endpoint_performance_test import ( - EndpointPerformanceTest, -) -from envs.monkey_zoo.blackbox.tests.performance.performance_test_config import PerformanceTestConfig -from envs.monkey_zoo.blackbox.tests.performance.telemetry_performance_test import ( - TelemetryPerformanceTest, -) - - -class TelemetryPerformanceTestWorkflow(BasicTest): - def __init__( - self, name, island_client, performance_config: PerformanceTestConfig, quick_performance_test - ): - self.name = name - self.island_client = island_client - self.performance_config = performance_config - self.quick_performance_test = quick_performance_test - - def run(self): - try: - if not self.quick_performance_test: - telem_sending_test = TelemetryPerformanceTest( - island_client=self.island_client, - quick_performance_test=self.quick_performance_test, - ) - telem_sending_test.test_telemetry_performance() - performance_test = EndpointPerformanceTest( - self.name, self.performance_config, self.island_client - ) - assert performance_test.run() - finally: - if not self.quick_performance_test: - self.island_client.reset_env() diff --git a/envs/monkey_zoo/blackbox/utils/config_generation_script.py b/envs/monkey_zoo/blackbox/utils/config_generation_script.py deleted file mode 100644 index f3d20d414..000000000 --- a/envs/monkey_zoo/blackbox/utils/config_generation_script.py +++ /dev/null @@ -1,59 +0,0 @@ -import argparse -import pathlib -from typing import Type - -from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate -from envs.monkey_zoo.blackbox.config_templates.depth_1_a import Depth1A -from envs.monkey_zoo.blackbox.config_templates.depth_2_a import Depth2A -from envs.monkey_zoo.blackbox.config_templates.depth_3_a import Depth3A -from envs.monkey_zoo.blackbox.config_templates.powershell_credentials_reuse import ( - PowerShellCredentialsReuse, -) -from envs.monkey_zoo.blackbox.config_templates.smb_pth import SmbPth -from envs.monkey_zoo.blackbox.config_templates.wmi_mimikatz import WmiMimikatz -from envs.monkey_zoo.blackbox.config_templates.zerologon import Zerologon -from envs.monkey_zoo.blackbox.island_client.island_config_parser import IslandConfigParser -from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient - -DST_DIR_NAME = "generated_configs" -DST_DIR_PATH = pathlib.Path(pathlib.Path(__file__).parent.absolute(), DST_DIR_NAME) - -parser = argparse.ArgumentParser(description="Generate config files.") -parser.add_argument( - "island_ip", - metavar="IP:PORT", - help="Island endpoint. Example: 123.123.123.123:5000", -) - -args = parser.parse_args() -island_client = MonkeyIslandClient(args.island_ip) - -CONFIG_TEMPLATES = [ - Depth1A, - Depth2A, - Depth3A, - Zerologon, - SmbPth, - WmiMimikatz, - PowerShellCredentialsReuse, -] - - -def generate_templates(): - for template in CONFIG_TEMPLATES: - save_template_as_config(template) - - -def save_template_as_config(template: Type[ConfigTemplate]): - file_path = pathlib.Path(DST_DIR_PATH, f"{template.__name__}.conf") - file_contents = IslandConfigParser.get_raw_config(template, island_client) - save_to_file(file_path, file_contents) - - -def save_to_file(file_path, contents): - with open(file_path, "w") as file: - file.write(contents) - - -if __name__ == "__main__": - generate_templates() diff --git a/monkey/common/configuration/agent_configuration.py b/monkey/common/configuration/agent_configuration.py index d6e61ddd1..11ed22718 100644 --- a/monkey/common/configuration/agent_configuration.py +++ b/monkey/common/configuration/agent_configuration.py @@ -79,13 +79,23 @@ class AgentConfiguration: except MarshmallowError as err: raise InvalidConfigurationError(str(err)) + @staticmethod + def to_mapping(config: AgentConfiguration) -> Mapping[str, Any]: + """ + Serialize an AgentConfiguration to a Mapping + + :param config: An AgentConfiguration + :return: A Mapping that represents the AgentConfiguration + """ + return AgentConfigurationSchema().dump(config) + @staticmethod def to_json(config: AgentConfiguration) -> str: """ Serialize an AgentConfiguration to JSON :param config: An AgentConfiguration - :return: A JSON string representing the AgentConfiguration + :return: A JSON string that represents the AgentConfiguration """ return AgentConfigurationSchema().dumps(config) diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py index 52b6bd30b..37cae6cdb 100644 --- a/monkey/monkey_island/cc/app.py +++ b/monkey/monkey_island/cc/app.py @@ -23,7 +23,6 @@ from monkey_island.cc.resources.agent_controls import StopAgentCheck, StopAllAge from monkey_island.cc.resources.attack.attack_report import AttackReport from monkey_island.cc.resources.auth.auth import Authenticate, init_jwt from monkey_island.cc.resources.auth.registration import Registration -from monkey_island.cc.resources.blackbox.clear_caches import ClearCaches from monkey_island.cc.resources.blackbox.log_blackbox_endpoint import LogBlackboxEndpoint from monkey_island.cc.resources.blackbox.monkey_blackbox_endpoint import MonkeyBlackboxEndpoint from monkey_island.cc.resources.blackbox.telemetry_blackbox_endpoint import ( @@ -195,7 +194,6 @@ def init_restful_endpoints(api: FlaskDIWrapper): # Note: Preferably, the API will provide a rich feature set and allow access to all of the # necessary data. This would make these endpoints obsolete. api.add_resource(MonkeyBlackboxEndpoint) - api.add_resource(ClearCaches) api.add_resource(LogBlackboxEndpoint) api.add_resource(TelemetryBlackboxEndpoint) diff --git a/monkey/monkey_island/cc/resources/agent_configuration.py b/monkey/monkey_island/cc/resources/agent_configuration.py index 47be16abc..36d6ce8e0 100644 --- a/monkey/monkey_island/cc/resources/agent_configuration.py +++ b/monkey/monkey_island/cc/resources/agent_configuration.py @@ -26,6 +26,7 @@ class AgentConfiguration(AbstractResource): try: configuration_object = AgentConfigurationObject.from_mapping(request.json) self._agent_configuration_repository.store_configuration(configuration_object) + # API Spec: Should return 204 (NO CONTENT) return make_response({}, 200) except (InvalidConfigurationError, json.JSONDecodeError) as err: return make_response( diff --git a/monkey/monkey_island/cc/resources/blackbox/clear_caches.py b/monkey/monkey_island/cc/resources/blackbox/clear_caches.py deleted file mode 100644 index af650655b..000000000 --- a/monkey/monkey_island/cc/resources/blackbox/clear_caches.py +++ /dev/null @@ -1,40 +0,0 @@ -import logging - -import flask_restful - -from monkey_island.cc.resources.AbstractResource import AbstractResource -from monkey_island.cc.resources.request_authentication 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(AbstractResource): - # API Spec: Action, not a resource; RPC-style endpoint? - urls = ["/api/test/clear-caches"] - """ - 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 get(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.error(NOT_ALL_REPORTS_DELETED) - flask_restful.abort(500, error_info=NOT_ALL_REPORTS_DELETED) - - return {"success": "true"} diff --git a/monkey/monkey_island/cc/resources/propagation_credentials.py b/monkey/monkey_island/cc/resources/propagation_credentials.py index 2342d0f37..f3cd05132 100644 --- a/monkey/monkey_island/cc/resources/propagation_credentials.py +++ b/monkey/monkey_island/cc/resources/propagation_credentials.py @@ -29,7 +29,7 @@ class PropagationCredentials(AbstractResource): return propagation_credentials, HTTPStatus.OK def post(self, collection=None): - credentials = [Credentials.from_json(c) for c in request.json] + credentials = [Credentials.from_mapping(c) for c in request.json] if collection == _configured_collection: self._credentials_repository.save_configured_credentials(credentials) diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py index 96a840cf9..764ac1542 100644 --- a/monkey/monkey_island/cc/services/attack/attack_report.py +++ b/monkey/monkey_island/cc/services/attack/attack_report.py @@ -137,14 +137,6 @@ class AttackReportService: generated_report = mongo.db.attack_report.find_one({}) return generated_report is not None - @staticmethod - def delete_saved_report_if_exists(): - 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 - ) - @staticmethod def get_techniques_for_report(): """ diff --git a/monkey/monkey_island/cc/services/reporting/report.py b/monkey/monkey_island/cc/services/reporting/report.py index 1a46a6566..89c6d2a78 100644 --- a/monkey/monkey_island/cc/services/reporting/report.py +++ b/monkey/monkey_island/cc/services/reporting/report.py @@ -499,19 +499,6 @@ 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 - """ - 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 - ) - @staticmethod def get_report(): if not ReportService.is_latest_report_exists(): diff --git a/monkey/monkey_island/cc/ui/src/components/configuration-components/ReformatHook.js b/monkey/monkey_island/cc/ui/src/components/configuration-components/ReformatHook.js index b5f538347..a26987b37 100644 --- a/monkey/monkey_island/cc/ui/src/components/configuration-components/ReformatHook.js +++ b/monkey/monkey_island/cc/ui/src/components/configuration-components/ReformatHook.js @@ -13,7 +13,12 @@ export function reformatConfig(config, reverse = false) { } formattedConfig['keep_tunnel_open_time'] = formattedConfig['advanced']['keep_tunnel_open_time']; } else { - formattedConfig['payloads'] = formattedConfig['payloads'][0]['options']; + if(formattedConfig['payloads'].length !== 0) + { + formattedConfig['payloads'] = formattedConfig['payloads'][0]['options']; + } else { + formattedConfig['payloads'] = {'encryption': {}, 'other_behaviors': {}} + } formattedConfig['advanced'] = {}; formattedConfig['advanced']['keep_tunnel_open_time'] = formattedConfig['keep_tunnel_open_time']; } diff --git a/monkey/tests/monkey_island/in_memory_agent_configuration_repository.py b/monkey/tests/monkey_island/in_memory_agent_configuration_repository.py index cf97ac08a..94bba3ddc 100644 --- a/monkey/tests/monkey_island/in_memory_agent_configuration_repository.py +++ b/monkey/tests/monkey_island/in_memory_agent_configuration_repository.py @@ -1,12 +1,10 @@ -from tests.common.example_agent_configuration import AGENT_CONFIGURATION - -from common.configuration.agent_configuration import AgentConfiguration +from common.configuration import DEFAULT_AGENT_CONFIGURATION from monkey_island.cc.repository import IAgentConfigurationRepository class InMemoryAgentConfigurationRepository(IAgentConfigurationRepository): def __init__(self): - self._default_configuration = AgentConfiguration.from_mapping(AGENT_CONFIGURATION) + self._default_configuration = DEFAULT_AGENT_CONFIGURATION self._configuration = self._default_configuration def get_configuration(self): diff --git a/monkey/tests/unit_tests/common/configuration/test_agent_configuration.py b/monkey/tests/unit_tests/common/configuration/test_agent_configuration.py index fecb6a6f6..928c8d0ec 100644 --- a/monkey/tests/unit_tests/common/configuration/test_agent_configuration.py +++ b/monkey/tests/unit_tests/common/configuration/test_agent_configuration.py @@ -28,7 +28,6 @@ from tests.common.example_agent_configuration import ( ) from common.configuration import AgentConfiguration, InvalidConfigurationError -from common.configuration.agent_configuration import AgentConfigurationSchema from common.configuration.agent_sub_configuration_schemas import ( CustomPBAConfigurationSchema, ExploitationConfigurationSchema, @@ -178,16 +177,13 @@ def test_incorrect_type(): AgentConfiguration(**valid_config_dict) -def test_from_dict(): - schema = AgentConfigurationSchema() - dict_ = deepcopy(AGENT_CONFIGURATION) +def test_to_from_mapping(): + config = AgentConfiguration.from_mapping(AGENT_CONFIGURATION) - config = AgentConfiguration.from_mapping(dict_) - - assert schema.dump(config) == dict_ + assert AgentConfiguration.to_mapping(config) == AGENT_CONFIGURATION -def test_from_dict__invalid_data(): +def test_from_mapping__invalid_data(): dict_ = deepcopy(AGENT_CONFIGURATION) dict_["payloads"] = "payloads" @@ -195,14 +191,11 @@ def test_from_dict__invalid_data(): AgentConfiguration.from_mapping(dict_) -def test_from_json(): - schema = AgentConfigurationSchema() - dict_ = deepcopy(AGENT_CONFIGURATION) +def test_to_from_json(): + original_config = AgentConfiguration.from_mapping(AGENT_CONFIGURATION) + config_json = AgentConfiguration.to_json(original_config) - config = AgentConfiguration.from_json(json.dumps(dict_)) - - assert isinstance(config, AgentConfiguration) - assert schema.dump(config) == dict_ + assert AgentConfiguration.from_json(config_json) == original_config def test_from_json__invalid_data(): @@ -211,9 +204,3 @@ def test_from_json__invalid_data(): with pytest.raises(InvalidConfigurationError): AgentConfiguration.from_json(json.dumps(invalid_dict)) - - -def test_to_json(): - config = deepcopy(AGENT_CONFIGURATION) - - assert json.loads(AgentConfiguration.to_json(config)) == AGENT_CONFIGURATION diff --git a/monkey/tests/unit_tests/monkey_island/cc/resources/test_agent_configuration.py b/monkey/tests/unit_tests/monkey_island/cc/resources/test_agent_configuration.py index 874500b81..917aaf562 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/resources/test_agent_configuration.py +++ b/monkey/tests/unit_tests/monkey_island/cc/resources/test_agent_configuration.py @@ -6,8 +6,13 @@ from tests.common.example_agent_configuration import AGENT_CONFIGURATION from tests.monkey_island import InMemoryAgentConfigurationRepository from tests.unit_tests.monkey_island.conftest import get_url_for_resource +from common.configuration import AgentConfiguration from monkey_island.cc.repository import IAgentConfigurationRepository -from monkey_island.cc.resources.agent_configuration import AgentConfiguration +from monkey_island.cc.resources.agent_configuration import ( + AgentConfiguration as AgentConfigurationResource, +) + +AGENT_CONFIGURATION_URL = get_url_for_resource(AgentConfigurationResource) @pytest.fixture @@ -21,30 +26,25 @@ def flask_client(build_flask_client): def test_agent_configuration_endpoint(flask_client): - agent_configuration_url = get_url_for_resource(AgentConfiguration) - - flask_client.post( - agent_configuration_url, data=json.dumps(AGENT_CONFIGURATION), follow_redirects=True + resp = flask_client.post( + AGENT_CONFIGURATION_URL, + json=AgentConfiguration.to_mapping(AGENT_CONFIGURATION), + follow_redirects=True, ) - resp = flask_client.get(agent_configuration_url) + assert resp.status_code == 200 + resp = flask_client.get(AGENT_CONFIGURATION_URL) assert resp.status_code == 200 assert json.loads(resp.data) == AGENT_CONFIGURATION def test_agent_configuration_invalid_config(flask_client): - agent_configuration_url = get_url_for_resource(AgentConfiguration) - - resp = flask_client.post( - agent_configuration_url, data=json.dumps({"invalid_config": "invalid_stuff"}) - ) + resp = flask_client.post(AGENT_CONFIGURATION_URL, json={"invalid_config": "invalid_stuff"}) assert resp.status_code == 400 def test_agent_configuration_invalid_json(flask_client): - agent_configuration_url = get_url_for_resource(AgentConfiguration) - - resp = flask_client.post(agent_configuration_url, data="InvalidJson!") + resp = flask_client.post(AGENT_CONFIGURATION_URL, data="InvalidJson!") assert resp.status_code == 400 diff --git a/monkey/tests/unit_tests/monkey_island/cc/resources/test_propagation_credentials.py b/monkey/tests/unit_tests/monkey_island/cc/resources/test_propagation_credentials.py index e613d85b5..37fdb131b 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/resources/test_propagation_credentials.py +++ b/monkey/tests/unit_tests/monkey_island/cc/resources/test_propagation_credentials.py @@ -89,8 +89,8 @@ def test_propagation_credentials_endpoint__post_stolen(flask_client, credentials resp = flask_client.post( url, json=[ - Credentials.to_json(LM_HASH_CREDENTIALS), - Credentials.to_json(NT_HASH_CREDENTIALS), + Credentials.to_mapping(LM_HASH_CREDENTIALS), + Credentials.to_mapping(NT_HASH_CREDENTIALS), ], ) assert resp.status_code == HTTPStatus.NO_CONTENT @@ -134,8 +134,8 @@ def test_propagation_credentials_endpoint__post_not_found(flask_client): resp = flask_client.post( NON_EXISTENT_COLLECTION_URL, json=[ - Credentials.to_json(LM_HASH_CREDENTIALS), - Credentials.to_json(NT_HASH_CREDENTIALS), + Credentials.to_mapping(LM_HASH_CREDENTIALS), + Credentials.to_mapping(NT_HASH_CREDENTIALS), ], ) assert resp.status_code == HTTPStatus.NOT_FOUND