Agent: Change scanners to use the config object

This commit is contained in:
vakarisz 2022-06-21 16:15:54 +03:00 committed by Mike Salvatore
parent 6b406ef686
commit ffe8c3451b
6 changed files with 130 additions and 87 deletions

View File

@ -2,6 +2,18 @@ from .agent_configuration import (
AgentConfiguration, AgentConfiguration,
AgentConfigurationSchema, AgentConfigurationSchema,
) )
from .agent_sub_configurations import (
CustomPBAConfiguration,
PluginConfiguration,
ScanTargetConfiguration,
ICMPScanConfiguration,
TCPScanConfiguration,
NetworkScanConfiguration,
ExploitationOptionsConfiguration,
ExploiterConfiguration,
ExploitationConfiguration,
PropagationConfiguration,
)
from .default_agent_configuration import ( from .default_agent_configuration import (
DEFAULT_AGENT_CONFIGURATION_JSON, DEFAULT_AGENT_CONFIGURATION_JSON,
build_default_agent_configuration, build_default_agent_configuration,

View File

@ -174,7 +174,7 @@ class AutomatedMaster(IMaster):
logger.info(f"Current depth is {current_depth}") logger.info(f"Current depth is {current_depth}")
if should_propagate(self._control_channel.get_config(), self._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: else:
logger.info("Skipping propagation: maximum depth reached") logger.info("Skipping propagation: maximum depth reached")

View File

@ -3,8 +3,13 @@ import queue
import threading import threading
from queue import Queue from queue import Queue
from threading import Event 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 ( from infection_monkey.i_puppet import (
FingerprintData, FingerprintData,
IPuppet, IPuppet,
@ -30,7 +35,7 @@ class IPScanner:
def scan( def scan(
self, self,
addresses_to_scan: List[NetworkAddress], addresses_to_scan: List[NetworkAddress],
options: Dict, options: ScanTargetConfiguration,
results_callback: Callback, results_callback: Callback,
stop: Event, stop: Event,
): ):
@ -49,12 +54,16 @@ class IPScanner:
) )
def _scan_addresses( 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()}") logger.debug(f"Starting scan .read -- Thread ID: {threading.get_ident()}")
icmp_timeout = options["icmp"]["timeout_ms"] / 1000 icmp_timeout = options.icmp.timeout
tcp_timeout = options["tcp"]["timeout_ms"] / 1000 tcp_timeout = options.tcp.timeout
tcp_ports = options["tcp"]["ports"] tcp_ports = options.tcp.ports
try: try:
while not stop.is_set(): while not stop.is_set():
@ -66,7 +75,7 @@ class IPScanner:
fingerprint_data = {} fingerprint_data = {}
if IPScanner.port_scan_found_open_port(port_scan_data): if IPScanner.port_scan_found_open_port(port_scan_data):
fingerprinters = options["fingerprinters"] fingerprinters = options.fingerprinters
fingerprint_data = self._run_fingerprinters( fingerprint_data = self._run_fingerprinters(
address.ip, fingerprinters, ping_scan_data, port_scan_data, stop address.ip, fingerprinters, ping_scan_data, port_scan_data, stop
) )
@ -90,7 +99,7 @@ class IPScanner:
def _run_fingerprinters( def _run_fingerprinters(
self, self,
ip: str, ip: str,
fingerprinters: List[Dict[str, Any]], fingerprinters: List[PluginConfiguration],
ping_scan_data: PingScanData, ping_scan_data: PingScanData,
port_scan_data: Dict[int, PortScanData], port_scan_data: Dict[int, PortScanData],
stop: Event, stop: Event,
@ -98,8 +107,8 @@ class IPScanner:
fingerprint_data = {} fingerprint_data = {}
for f in interruptible_iter(fingerprinters, stop): for f in interruptible_iter(fingerprinters, stop):
fingerprint_data[f["name"]] = self._puppet.fingerprint( fingerprint_data[f.name] = self._puppet.fingerprint(
f["name"], ip, ping_scan_data, port_scan_data, f["options"] f.name, ip, ping_scan_data, port_scan_data, f.options
) )
return fingerprint_data return fingerprint_data

View File

@ -3,6 +3,8 @@ from queue import Queue
from threading import Event from threading import Event
from typing import Dict, List from typing import Dict, List
from common.configuration import PropagationConfiguration,\
NetworkScanConfiguration, ScanTargetConfiguration
from infection_monkey.i_puppet import ( from infection_monkey.i_puppet import (
ExploiterResultData, ExploiterResultData,
FingerprintData, FingerprintData,
@ -39,14 +41,18 @@ class Propagator:
self._local_network_interfaces = local_network_interfaces self._local_network_interfaces = local_network_interfaces
self._hosts_to_exploit = None 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") logger.info("Attempting to propagate")
network_scan_completed = Event() network_scan_completed = Event()
self._hosts_to_exploit = Queue() self._hosts_to_exploit = Queue()
scan_thread = create_daemon_thread( 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( exploit_thread = create_daemon_thread(
target=self._exploit_hosts, target=self._exploit_hosts,
@ -64,22 +70,21 @@ class Propagator:
logger.info("Finished attempting to propagate") 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") logger.info("Starting network scan")
target_config = propagation_config["targets"] addresses_to_scan = self._compile_scan_target_list(scan_config.targets)
scan_config = propagation_config["network_scan"]
addresses_to_scan = self._compile_scan_target_list(target_config)
self._ip_scanner.scan(addresses_to_scan, scan_config, self._process_scan_results, stop) self._ip_scanner.scan(addresses_to_scan, scan_config, self._process_scan_results, stop)
logger.info("Finished network scan") logger.info("Finished network scan")
def _compile_scan_target_list(self, target_config: Dict) -> List[NetworkAddress]: def _compile_scan_target_list(
ranges_to_scan = target_config["subnet_scan_list"] self, target_config: ScanTargetConfiguration
inaccessible_subnets = target_config["inaccessible_subnets"] ) -> List[NetworkAddress]:
blocklisted_ips = target_config["blocked_ips"] ranges_to_scan = target_config.subnets
enable_local_network_scan = target_config["local_network_scan"] 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( return compile_scan_target_list(
self._local_network_interfaces, self._local_network_interfaces,

View File

@ -5,6 +5,12 @@ from unittest.mock import MagicMock
import pytest import pytest
from tests.unit_tests.infection_monkey.master.mock_puppet import MockPuppet 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.i_puppet import FingerprintData, PortScanData, PortStatus
from infection_monkey.master import IPScanner from infection_monkey.master import IPScanner
from infection_monkey.network import NetworkAddress from infection_monkey.network import NetworkAddress
@ -14,28 +20,31 @@ LINUX_OS = "linux"
@pytest.fixture @pytest.fixture
def scan_config(): def scan_config(default_agent_config):
return { tcp_config = TCPScanConfiguration(
"tcp": { timeout=3,
"timeout_ms": 3000, ports=[
"ports": [ 22,
22, 445,
445, 3389,
3389, 443,
443, 8008,
8008, 3306,
3306,
],
},
"icmp": {
"timeout_ms": 1000,
},
"fingerprinters": [
{"name": "HTTPFinger", "options": {}},
{"name": "SMBFinger", "options": {}},
{"name": "SSHFinger", "options": {}},
], ],
} )
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 @pytest.fixture

View File

@ -3,6 +3,11 @@ from unittest.mock import MagicMock
import pytest import pytest
from common.configuration.agent_sub_configurations import (
NetworkScanConfiguration,
PropagationConfiguration,
ScanTargetConfiguration,
)
from infection_monkey.i_puppet import ( from infection_monkey.i_puppet import (
ExploiterResultData, ExploiterResultData,
FingerprintData, FingerprintData,
@ -135,24 +140,35 @@ class StubExploiter:
pass 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( p = Propagator(
telemetry_messenger_spy, mock_ip_scanner, StubExploiter(), mock_victim_host_factory, [] telemetry_messenger_spy, mock_ip_scanner, StubExploiter(), mock_victim_host_factory, []
) )
p.propagate( targets = ScanTargetConfiguration(
{ blocked_ips=[],
"targets": { inaccessible_subnets=[],
"subnet_scan_list": ["10.0.0.1", "10.0.0.2", "10.0.0.3"], local_network_scan=False,
"local_network_scan": False, subnets=["10.0.0.1", "10.0.0.2", "10.0.0.3"],
"inaccessible_subnets": [],
"blocked_ips": [],
},
"network_scan": {}, # This is empty since MockIPscanner ignores it
"exploiters": {}, # This is empty since StubExploiter ignores it
},
1,
Event(),
) )
propagation_config = get_propagation_config(default_agent_config, targets)
p.propagate(propagation_config, 1, Event())
assert len(telemetry_messenger_spy.telemetries) == 3 assert len(telemetry_messenger_spy.telemetries) == 3
@ -237,25 +253,20 @@ class MockExploiter:
def test_exploiter_result_processing( 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( p = Propagator(
telemetry_messenger_spy, mock_ip_scanner, MockExploiter(), mock_victim_host_factory, [] telemetry_messenger_spy, mock_ip_scanner, MockExploiter(), mock_victim_host_factory, []
) )
p.propagate(
{ targets = ScanTargetConfiguration(
"targets": { blocked_ips=[],
"subnet_scan_list": ["10.0.0.1", "10.0.0.2", "10.0.0.3"], inaccessible_subnets=[],
"local_network_scan": False, local_network_scan=False,
"inaccessible_subnets": [], subnets=["10.0.0.1", "10.0.0.2", "10.0.0.3"],
"blocked_ips": [],
},
"network_scan": {}, # This is empty since MockIPscanner ignores it
"exploiters": {}, # This is empty since MockExploiter ignores it
},
1,
Event(),
) )
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)] exploit_telems = [t for t in telemetry_messenger_spy.telemetries if isinstance(t, ExploitTelem)]
assert len(exploit_telems) == 4 assert len(exploit_telems) == 4
@ -278,7 +289,9 @@ def test_exploiter_result_processing(
assert data["propagation_result"] 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")] local_network_interfaces = [NetworkInterface("10.0.0.9", "/29")]
p = Propagator( p = Propagator(
telemetry_messenger_spy, telemetry_messenger_spy,
@ -287,20 +300,15 @@ def test_scan_target_generation(telemetry_messenger_spy, mock_ip_scanner, mock_v
mock_victim_host_factory, mock_victim_host_factory,
local_network_interfaces, local_network_interfaces,
) )
p.propagate( targets = ScanTargetConfiguration(
{ blocked_ips=["10.0.0.3"],
"targets": { inaccessible_subnets=["10.0.0.128/30", "10.0.0.8/29"],
"subnet_scan_list": ["10.0.0.0/29", "172.10.20.30"], local_network_scan=True,
"local_network_scan": True, subnets=["10.0.0.0/29", "172.10.20.30"],
"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(),
) )
propagation_config = get_propagation_config(default_agent_config, targets)
p.propagate(propagation_config, 1, Event())
expected_ip_scan_list = [ expected_ip_scan_list = [
"10.0.0.0", "10.0.0.0",
"10.0.0.1", "10.0.0.1",