Agent: Change scanners to use the config object
This commit is contained in:
parent
6b406ef686
commit
ffe8c3451b
|
@ -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,
|
||||||
|
|
|
@ -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")
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Reference in New Issue