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.
This commit is contained in:
Mike Salvatore 2022-02-17 12:36:17 -05:00
parent 095572f919
commit 7551f254fc
4 changed files with 49 additions and 7 deletions

View File

@ -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,

View File

@ -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):

View File

@ -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()

View File

@ -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