diff --git a/envs/monkey_zoo/blackbox/analyzers/analyzer.py b/envs/monkey_zoo/blackbox/analyzers/analyzer.py index d6043feeb..13db46cb3 100644 --- a/envs/monkey_zoo/blackbox/analyzers/analyzer.py +++ b/envs/monkey_zoo/blackbox/analyzers/analyzer.py @@ -4,5 +4,5 @@ from abc import ABCMeta, abstractmethod class Analyzer(object, metaclass=ABCMeta): @abstractmethod - def analyze_test_results(self): + def analyze_test_results(self) -> bool: raise NotImplementedError() diff --git a/envs/monkey_zoo/blackbox/analyzers/zerologon_analyzer.py b/envs/monkey_zoo/blackbox/analyzers/zerologon_analyzer.py new file mode 100644 index 000000000..691724830 --- /dev/null +++ b/envs/monkey_zoo/blackbox/analyzers/zerologon_analyzer.py @@ -0,0 +1,42 @@ +from typing import List + +import dpath.util + +from common.config_value_paths import USER_LIST_PATH, PASSWORD_LIST_PATH, NTLM_HASH_LIST_PATH, LM_HASH_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 + +# Query for telemetry collection to see if password restoration was successful +TELEM_QUERY = {'telem_category': 'exploit', + 'data.exploiter': 'ZerologonExploiter', + 'data.info.password_restored': True} + + +class ZeroLogonAnalyzer(Analyzer): + + def __init__(self, island_client: MonkeyIslandClient, expected_credentials: List[str]): + self.island_client = island_client + self.expected_credentials = expected_credentials + self.log = AnalyzerLog(self.__class__.__name__) + + def analyze_test_results(self): + self.log.clear() + return self._analyze_credential_gathering() and self._analyze_credential_restore() + + def _analyze_credential_gathering(self) -> bool: + credentials_on_island = [] + config = self.island_client.get_config() + 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 ZeroLogonAnalyzer._is_all_credentials_in_list(self.expected_credentials, + credentials_on_island) + + @staticmethod + def _is_all_credentials_in_list(expected_creds: List[str], + all_creds: List[str]) -> bool: + return all((cred in all_creds) for cred in expected_creds) + + def _analyze_credential_restore(self) -> bool: + return bool(self.island_client.find_telems_in_db(TELEM_QUERY)) diff --git a/envs/monkey_zoo/blackbox/island_configs/zerologon.py b/envs/monkey_zoo/blackbox/island_configs/zerologon.py new file mode 100644 index 000000000..725fa91b9 --- /dev/null +++ b/envs/monkey_zoo/blackbox/island_configs/zerologon.py @@ -0,0 +1,13 @@ +from copy import copy + +from envs.monkey_zoo.blackbox.island_configs.base_template import BaseTemplate + + +class ZeroLogon(BaseTemplate): + + config_values = copy(BaseTemplate.config_values) + + config_values.update({ + "basic.exploiters.exploiter_classes": ["ZerologonExploiter"], + "basic_network.scope.subnet_scan_list": ["10.2.2.25"] + }) diff --git a/envs/monkey_zoo/blackbox/test_blackbox.py b/envs/monkey_zoo/blackbox/test_blackbox.py index d895f7cfe..7560b5d42 100644 --- a/envs/monkey_zoo/blackbox/test_blackbox.py +++ b/envs/monkey_zoo/blackbox/test_blackbox.py @@ -7,6 +7,7 @@ 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.island_client.island_config_parser import \ IslandConfigParser from envs.monkey_zoo.blackbox.island_client.monkey_island_client import \ @@ -25,6 +26,7 @@ from envs.monkey_zoo.blackbox.island_configs.tunneling import Tunneling from envs.monkey_zoo.blackbox.island_configs.weblogic import Weblogic from envs.monkey_zoo.blackbox.island_configs.wmi_mimikatz import WmiMimikatz from envs.monkey_zoo.blackbox.island_configs.wmi_pth import WmiPth +from envs.monkey_zoo.blackbox.island_configs.zerologon import ZeroLogon from envs.monkey_zoo.blackbox.log_handlers.test_logs_handler import \ TestLogsHandler from envs.monkey_zoo.blackbox.tests.exploitation import ExploitationTest @@ -160,6 +162,21 @@ class TestMonkeyBlackbox: def test_wmi_pth(self, island_client): TestMonkeyBlackbox.run_exploitation_test(island_client, WmiPth, "WMI_PTH") + def test_zerologon_exploiter(self, island_client): + test_name = "ZeroLogon_exploiter" + expected_creds = ["test_username", "test_ntlm_hash"] + raw_config = IslandConfigParser.get_raw_config(ZeroLogon, island_client) + analyzer = ZeroLogonAnalyzer(island_client, expected_creds) + log_handler = TestLogsHandler(test_name, island_client, TestMonkeyBlackbox.get_log_dir_path()) + ExploitationTest( + name=test_name, + island_client=island_client, + raw_config=raw_config, + analyzers=[analyzer], + timeout=DEFAULT_TIMEOUT_SECONDS, + log_handler=log_handler).run() + TestMonkeyBlackbox.run_exploitation_test(island_client, ZeroLogon, "ZeroLogon_exploiter") + @pytest.mark.skip(reason="Perfomance test that creates env from fake telemetries is faster, use that instead.") def test_report_generation_performance(self, island_client, quick_performance_tests): """