From 7551f254fc7df05e484c249d0b5a4817a2a2d895 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 17 Feb 2022 12:36:17 -0500 Subject: [PATCH] Agent: Query for updated credentials in Exploiter Allows exploiters to be run with the most up-to-date configured and stolen credentials from the Island. --- .../master/automated_master.py | 4 ++- monkey/infection_monkey/master/exploiter.py | 25 ++++++++++++++++--- .../master/test_automated_master.py | 2 +- .../infection_monkey/master/test_exploiter.py | 25 +++++++++++++++++-- 4 files changed, 49 insertions(+), 7 deletions(-) diff --git a/monkey/infection_monkey/master/automated_master.py b/monkey/infection_monkey/master/automated_master.py index 9fbb5f200..d78d5aafe 100644 --- a/monkey/infection_monkey/master/automated_master.py +++ b/monkey/infection_monkey/master/automated_master.py @@ -41,7 +41,9 @@ class AutomatedMaster(IMaster): self._control_channel = control_channel ip_scanner = IPScanner(self._puppet, NUM_SCAN_THREADS) - exploiter = Exploiter(self._puppet, NUM_EXPLOIT_THREADS) + exploiter = Exploiter( + self._puppet, NUM_EXPLOIT_THREADS, self._control_channel.get_credentials_for_propagation + ) self._propagator = Propagator( self._telemetry_messenger, ip_scanner, diff --git a/monkey/infection_monkey/master/exploiter.py b/monkey/infection_monkey/master/exploiter.py index 09f6ebf4b..9d5fe4f00 100644 --- a/monkey/infection_monkey/master/exploiter.py +++ b/monkey/infection_monkey/master/exploiter.py @@ -3,7 +3,7 @@ import queue import threading from queue import Queue from threading import Event -from typing import Callable, Dict, List +from typing import Callable, Dict, List, Mapping from infection_monkey.i_puppet import ExploiterResultData, IPuppet from infection_monkey.model import VictimHost @@ -18,9 +18,15 @@ Callback = Callable[[ExploiterName, VictimHost, ExploiterResultData], None] class Exploiter: - def __init__(self, puppet: IPuppet, num_workers: int): + def __init__( + self, + puppet: IPuppet, + num_workers: int, + get_updated_credentials_for_propagation: Callable[[], Mapping], + ): self._puppet = puppet self._num_workers = num_workers + self._get_updated_credentials_for_propagation = get_updated_credentials_for_propagation def exploit_hosts( self, @@ -74,6 +80,7 @@ class Exploiter: results_callback: Callback, stop: Event, ): + for exploiter in interruptable_iter(exploiters_to_run, stop): exploiter_name = exploiter["name"] exploiter_results = self._run_exploiter(exploiter_name, victim_host, stop) @@ -86,7 +93,19 @@ class Exploiter: self, exploiter_name: str, victim_host: VictimHost, stop: Event ) -> ExploiterResultData: logger.debug(f"Attempting to use {exploiter_name} on {victim_host}") - return self._puppet.exploit_host(exploiter_name, victim_host.ip_addr, {}, stop) + + credentials = self._get_credentials_for_propagation() + options = {"credentials": credentials} + + return self._puppet.exploit_host(exploiter_name, victim_host.ip_addr, options, stop) + + def _get_credentials_for_propagation(self) -> Mapping: + try: + return self._get_updated_credentials_for_propagation() + except Exception as ex: + logger.error(f"Error while attempting to retrieve credentials for propagation: {ex}") + + return {} def _all_hosts_have_been_processed(scan_completed: Event, hosts_to_exploit: Queue): diff --git a/monkey/tests/unit_tests/infection_monkey/master/test_automated_master.py b/monkey/tests/unit_tests/infection_monkey/master/test_automated_master.py index d08a4465a..c7023e525 100644 --- a/monkey/tests/unit_tests/infection_monkey/master/test_automated_master.py +++ b/monkey/tests/unit_tests/infection_monkey/master/test_automated_master.py @@ -14,7 +14,7 @@ INTERVAL = 0.001 def test_terminate_without_start(): - m = AutomatedMaster(None, None, None, None, []) + m = AutomatedMaster(None, None, None, MagicMock(), []) # Test that call to terminate does not raise exception m.terminate() diff --git a/monkey/tests/unit_tests/infection_monkey/master/test_exploiter.py b/monkey/tests/unit_tests/infection_monkey/master/test_exploiter.py index 5b9297fe6..b2c42f1ec 100644 --- a/monkey/tests/unit_tests/infection_monkey/master/test_exploiter.py +++ b/monkey/tests/unit_tests/infection_monkey/master/test_exploiter.py @@ -59,11 +59,18 @@ def hosts_to_exploit(hosts): return q +CREDENTIALS_FOR_PROPAGATION = {"usernames": ["m0nk3y", "user"], "passwords": ["1234", "pword"]} + + +def get_credentials_for_propagation(): + return CREDENTIALS_FOR_PROPAGATION + + def test_exploiter(exploiter_config, callback, scan_completed, stop, hosts, hosts_to_exploit): # Set this so that Exploiter() exits once it has processed all victims scan_completed.set() - e = Exploiter(MockPuppet(), 2) + e = Exploiter(MockPuppet(), 2, get_credentials_for_propagation) e.exploit_hosts(exploiter_config, hosts_to_exploit, callback, scan_completed, stop) assert callback.call_count == 5 @@ -81,6 +88,20 @@ def test_exploiter(exploiter_config, callback, scan_completed, stop, hosts, host assert ("SSHExploiter", hosts[1]) in host_exploit_combos +def test_credentials_passed_to_exploiter( + exploiter_config, callback, scan_completed, stop, hosts, hosts_to_exploit +): + mock_puppet = MagicMock() + # Set this so that Exploiter() exits once it has processed all victims + scan_completed.set() + + e = Exploiter(mock_puppet, 2, get_credentials_for_propagation) + e.exploit_hosts(exploiter_config, hosts_to_exploit, callback, scan_completed, stop) + + for call_args in mock_puppet.exploit_host.call_args_list: + assert call_args[0][2].get("credentials") == CREDENTIALS_FOR_PROPAGATION + + def test_stop_after_callback(exploiter_config, callback, scan_completed, stop, hosts_to_exploit): callback_barrier_count = 2 @@ -96,7 +117,7 @@ def test_stop_after_callback(exploiter_config, callback, scan_completed, stop, h # Intentionally NOT setting scan_completed.set(); _callback() will set stop - e = Exploiter(MockPuppet(), callback_barrier_count + 2) + e = Exploiter(MockPuppet(), callback_barrier_count + 2, get_credentials_for_propagation) e.exploit_hosts(exploiter_config, hosts_to_exploit, stoppable_callback, scan_completed, stop) assert stoppable_callback.call_count == 2