From ffe8c3451b9bb1a01f0b8d94afedbc81809dacea Mon Sep 17 00:00:00 2001 From: vakarisz Date: Tue, 21 Jun 2022 16:15:54 +0300 Subject: [PATCH] Agent: Change scanners to use the config object --- monkey/common/configuration/__init__.py | 12 +++ .../master/automated_master.py | 2 +- monkey/infection_monkey/master/ip_scanner.py | 31 ++++--- monkey/infection_monkey/master/propagator.py | 29 +++--- .../master/test_ip_scanner.py | 51 +++++----- .../master/test_propagator.py | 92 ++++++++++--------- 6 files changed, 130 insertions(+), 87 deletions(-) diff --git a/monkey/common/configuration/__init__.py b/monkey/common/configuration/__init__.py index 2246e27f2..107e5a491 100644 --- a/monkey/common/configuration/__init__.py +++ b/monkey/common/configuration/__init__.py @@ -2,6 +2,18 @@ from .agent_configuration import ( AgentConfiguration, AgentConfigurationSchema, ) +from .agent_sub_configurations import ( + CustomPBAConfiguration, + PluginConfiguration, + ScanTargetConfiguration, + ICMPScanConfiguration, + TCPScanConfiguration, + NetworkScanConfiguration, + ExploitationOptionsConfiguration, + ExploiterConfiguration, + ExploitationConfiguration, + PropagationConfiguration, +) from .default_agent_configuration import ( DEFAULT_AGENT_CONFIGURATION_JSON, build_default_agent_configuration, diff --git a/monkey/infection_monkey/master/automated_master.py b/monkey/infection_monkey/master/automated_master.py index 909d36de6..573299629 100644 --- a/monkey/infection_monkey/master/automated_master.py +++ b/monkey/infection_monkey/master/automated_master.py @@ -174,7 +174,7 @@ class AutomatedMaster(IMaster): logger.info(f"Current depth is {current_depth}") if should_propagate(self._control_channel.get_config(), self._current_depth): - self._propagator.propagate(config["propagation"], current_depth, self._stop) + self._propagator.propagate(config.propagation, current_depth, self._stop) else: logger.info("Skipping propagation: maximum depth reached") diff --git a/monkey/infection_monkey/master/ip_scanner.py b/monkey/infection_monkey/master/ip_scanner.py index 8c0ea5caa..14391765f 100644 --- a/monkey/infection_monkey/master/ip_scanner.py +++ b/monkey/infection_monkey/master/ip_scanner.py @@ -3,8 +3,13 @@ import queue import threading from queue import Queue from threading import Event -from typing import Any, Callable, Dict, List +from typing import Callable, Dict, List +from common.configuration.agent_sub_configurations import ( + NetworkScanConfiguration, + PluginConfiguration, + ScanTargetConfiguration, +) from infection_monkey.i_puppet import ( FingerprintData, IPuppet, @@ -30,7 +35,7 @@ class IPScanner: def scan( self, addresses_to_scan: List[NetworkAddress], - options: Dict, + options: ScanTargetConfiguration, results_callback: Callback, stop: Event, ): @@ -49,12 +54,16 @@ class IPScanner: ) def _scan_addresses( - self, addresses: Queue, options: Dict, results_callback: Callback, stop: Event + self, + addresses: Queue, + options: NetworkScanConfiguration, + results_callback: Callback, + stop: Event, ): - logger.debug(f"Starting scan thread -- Thread ID: {threading.get_ident()}") - icmp_timeout = options["icmp"]["timeout_ms"] / 1000 - tcp_timeout = options["tcp"]["timeout_ms"] / 1000 - tcp_ports = options["tcp"]["ports"] + logger.debug(f"Starting scan .read -- Thread ID: {threading.get_ident()}") + icmp_timeout = options.icmp.timeout + tcp_timeout = options.tcp.timeout + tcp_ports = options.tcp.ports try: while not stop.is_set(): @@ -66,7 +75,7 @@ class IPScanner: fingerprint_data = {} if IPScanner.port_scan_found_open_port(port_scan_data): - fingerprinters = options["fingerprinters"] + fingerprinters = options.fingerprinters fingerprint_data = self._run_fingerprinters( address.ip, fingerprinters, ping_scan_data, port_scan_data, stop ) @@ -90,7 +99,7 @@ class IPScanner: def _run_fingerprinters( self, ip: str, - fingerprinters: List[Dict[str, Any]], + fingerprinters: List[PluginConfiguration], ping_scan_data: PingScanData, port_scan_data: Dict[int, PortScanData], stop: Event, @@ -98,8 +107,8 @@ class IPScanner: fingerprint_data = {} for f in interruptible_iter(fingerprinters, stop): - fingerprint_data[f["name"]] = self._puppet.fingerprint( - f["name"], ip, ping_scan_data, port_scan_data, f["options"] + fingerprint_data[f.name] = self._puppet.fingerprint( + f.name, ip, ping_scan_data, port_scan_data, f.options ) return fingerprint_data diff --git a/monkey/infection_monkey/master/propagator.py b/monkey/infection_monkey/master/propagator.py index be4d6caf2..a74cf7c86 100644 --- a/monkey/infection_monkey/master/propagator.py +++ b/monkey/infection_monkey/master/propagator.py @@ -3,6 +3,8 @@ from queue import Queue from threading import Event from typing import Dict, List +from common.configuration import PropagationConfiguration,\ + NetworkScanConfiguration, ScanTargetConfiguration from infection_monkey.i_puppet import ( ExploiterResultData, FingerprintData, @@ -39,14 +41,18 @@ class Propagator: self._local_network_interfaces = local_network_interfaces self._hosts_to_exploit = None - def propagate(self, propagation_config: Dict, current_depth: int, stop: Event): + def propagate( + self, propagation_config: PropagationConfiguration, current_depth: int, stop: Event + ): logger.info("Attempting to propagate") network_scan_completed = Event() self._hosts_to_exploit = Queue() scan_thread = create_daemon_thread( - target=self._scan_network, name="PropagatorScanThread", args=(propagation_config, stop) + target=self._scan_network, + name="PropagatorScanThread", + args=(propagation_config.network_scan, stop), ) exploit_thread = create_daemon_thread( target=self._exploit_hosts, @@ -64,22 +70,21 @@ class Propagator: logger.info("Finished attempting to propagate") - def _scan_network(self, propagation_config: Dict, stop: Event): + def _scan_network(self, scan_config: NetworkScanConfiguration, stop: Event): logger.info("Starting network scan") - target_config = propagation_config["targets"] - scan_config = propagation_config["network_scan"] - - addresses_to_scan = self._compile_scan_target_list(target_config) + addresses_to_scan = self._compile_scan_target_list(scan_config.targets) self._ip_scanner.scan(addresses_to_scan, scan_config, self._process_scan_results, stop) logger.info("Finished network scan") - def _compile_scan_target_list(self, target_config: Dict) -> List[NetworkAddress]: - ranges_to_scan = target_config["subnet_scan_list"] - inaccessible_subnets = target_config["inaccessible_subnets"] - blocklisted_ips = target_config["blocked_ips"] - enable_local_network_scan = target_config["local_network_scan"] + def _compile_scan_target_list( + self, target_config: ScanTargetConfiguration + ) -> List[NetworkAddress]: + ranges_to_scan = target_config.subnets + inaccessible_subnets = target_config.inaccessible_subnets + blocklisted_ips = target_config.blocked_ips + enable_local_network_scan = target_config.local_network_scan return compile_scan_target_list( self._local_network_interfaces, diff --git a/monkey/tests/unit_tests/infection_monkey/master/test_ip_scanner.py b/monkey/tests/unit_tests/infection_monkey/master/test_ip_scanner.py index 9fafdaee2..069f931b9 100644 --- a/monkey/tests/unit_tests/infection_monkey/master/test_ip_scanner.py +++ b/monkey/tests/unit_tests/infection_monkey/master/test_ip_scanner.py @@ -5,6 +5,12 @@ from unittest.mock import MagicMock import pytest from tests.unit_tests.infection_monkey.master.mock_puppet import MockPuppet +from common.configuration.agent_sub_configurations import ( + ICMPScanConfiguration, + NetworkScanConfiguration, + PluginConfiguration, + TCPScanConfiguration, +) from infection_monkey.i_puppet import FingerprintData, PortScanData, PortStatus from infection_monkey.master import IPScanner from infection_monkey.network import NetworkAddress @@ -14,28 +20,31 @@ LINUX_OS = "linux" @pytest.fixture -def scan_config(): - return { - "tcp": { - "timeout_ms": 3000, - "ports": [ - 22, - 445, - 3389, - 443, - 8008, - 3306, - ], - }, - "icmp": { - "timeout_ms": 1000, - }, - "fingerprinters": [ - {"name": "HTTPFinger", "options": {}}, - {"name": "SMBFinger", "options": {}}, - {"name": "SSHFinger", "options": {}}, +def scan_config(default_agent_config): + tcp_config = TCPScanConfiguration( + timeout=3, + ports=[ + 22, + 445, + 3389, + 443, + 8008, + 3306, ], - } + ) + icmp_config = ICMPScanConfiguration(timeout=1) + fingerprinter_config = [ + PluginConfiguration(name="HTTPFinger", options={}), + PluginConfiguration(name="SMBFinger", options={}), + PluginConfiguration(name="SSHFinger", options={}), + ] + scan_config = NetworkScanConfiguration( + tcp_config, + icmp_config, + fingerprinter_config, + default_agent_config.propagation.network_scan.targets, + ) + return scan_config @pytest.fixture diff --git a/monkey/tests/unit_tests/infection_monkey/master/test_propagator.py b/monkey/tests/unit_tests/infection_monkey/master/test_propagator.py index 3746e65eb..a2581de60 100644 --- a/monkey/tests/unit_tests/infection_monkey/master/test_propagator.py +++ b/monkey/tests/unit_tests/infection_monkey/master/test_propagator.py @@ -3,6 +3,11 @@ from unittest.mock import MagicMock import pytest +from common.configuration.agent_sub_configurations import ( + NetworkScanConfiguration, + PropagationConfiguration, + ScanTargetConfiguration, +) from infection_monkey.i_puppet import ( ExploiterResultData, FingerprintData, @@ -135,24 +140,35 @@ class StubExploiter: pass -def test_scan_result_processing(telemetry_messenger_spy, mock_ip_scanner, mock_victim_host_factory): +def get_propagation_config(default_agent_config, scan_target_config: ScanTargetConfiguration): + network_scan = NetworkScanConfiguration( + default_agent_config.propagation.network_scan.tcp, + default_agent_config.propagation.network_scan.icmp, + default_agent_config.propagation.network_scan.fingerprinters, + scan_target_config, + ) + propagation_config = PropagationConfiguration( + default_agent_config.propagation.maximum_depth, + network_scan, + default_agent_config.propagation.exploitation, + ) + return propagation_config + + +def test_scan_result_processing( + telemetry_messenger_spy, mock_ip_scanner, mock_victim_host_factory, default_agent_config +): p = Propagator( telemetry_messenger_spy, mock_ip_scanner, StubExploiter(), mock_victim_host_factory, [] ) - p.propagate( - { - "targets": { - "subnet_scan_list": ["10.0.0.1", "10.0.0.2", "10.0.0.3"], - "local_network_scan": False, - "inaccessible_subnets": [], - "blocked_ips": [], - }, - "network_scan": {}, # This is empty since MockIPscanner ignores it - "exploiters": {}, # This is empty since StubExploiter ignores it - }, - 1, - Event(), + targets = ScanTargetConfiguration( + blocked_ips=[], + inaccessible_subnets=[], + local_network_scan=False, + subnets=["10.0.0.1", "10.0.0.2", "10.0.0.3"], ) + propagation_config = get_propagation_config(default_agent_config, targets) + p.propagate(propagation_config, 1, Event()) assert len(telemetry_messenger_spy.telemetries) == 3 @@ -237,25 +253,20 @@ class MockExploiter: def test_exploiter_result_processing( - telemetry_messenger_spy, mock_ip_scanner, mock_victim_host_factory + telemetry_messenger_spy, mock_ip_scanner, mock_victim_host_factory, default_agent_config ): p = Propagator( telemetry_messenger_spy, mock_ip_scanner, MockExploiter(), mock_victim_host_factory, [] ) - p.propagate( - { - "targets": { - "subnet_scan_list": ["10.0.0.1", "10.0.0.2", "10.0.0.3"], - "local_network_scan": False, - "inaccessible_subnets": [], - "blocked_ips": [], - }, - "network_scan": {}, # This is empty since MockIPscanner ignores it - "exploiters": {}, # This is empty since MockExploiter ignores it - }, - 1, - Event(), + + targets = ScanTargetConfiguration( + blocked_ips=[], + inaccessible_subnets=[], + local_network_scan=False, + subnets=["10.0.0.1", "10.0.0.2", "10.0.0.3"], ) + propagation_config = get_propagation_config(default_agent_config, targets) + p.propagate(propagation_config, 1, Event()) exploit_telems = [t for t in telemetry_messenger_spy.telemetries if isinstance(t, ExploitTelem)] assert len(exploit_telems) == 4 @@ -278,7 +289,9 @@ def test_exploiter_result_processing( assert data["propagation_result"] -def test_scan_target_generation(telemetry_messenger_spy, mock_ip_scanner, mock_victim_host_factory): +def test_scan_target_generation( + telemetry_messenger_spy, mock_ip_scanner, mock_victim_host_factory, default_agent_config +): local_network_interfaces = [NetworkInterface("10.0.0.9", "/29")] p = Propagator( telemetry_messenger_spy, @@ -287,20 +300,15 @@ def test_scan_target_generation(telemetry_messenger_spy, mock_ip_scanner, mock_v mock_victim_host_factory, local_network_interfaces, ) - p.propagate( - { - "targets": { - "subnet_scan_list": ["10.0.0.0/29", "172.10.20.30"], - "local_network_scan": True, - "blocked_ips": ["10.0.0.3"], - "inaccessible_subnets": ["10.0.0.128/30", "10.0.0.8/29"], - }, - "network_scan": {}, # This is empty since MockIPscanner ignores it - "exploiters": {}, # This is empty since MockExploiter ignores it - }, - 1, - Event(), + targets = ScanTargetConfiguration( + blocked_ips=["10.0.0.3"], + inaccessible_subnets=["10.0.0.128/30", "10.0.0.8/29"], + local_network_scan=True, + subnets=["10.0.0.0/29", "172.10.20.30"], ) + propagation_config = get_propagation_config(default_agent_config, targets) + p.propagate(propagation_config, 1, Event()) + expected_ip_scan_list = [ "10.0.0.0", "10.0.0.1",