From 8067dc9ff86b5882656cf3a0be7b25228f6d501d Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 13 Dec 2021 13:06:44 -0500 Subject: [PATCH] Agent: Process fingerprinter results in Propagator --- monkey/infection_monkey/master/propagator.py | 39 +++++++++++--- .../master/test_propagator.py | 53 ++++++++++++++++--- 2 files changed, 79 insertions(+), 13 deletions(-) diff --git a/monkey/infection_monkey/master/propagator.py b/monkey/infection_monkey/master/propagator.py index da36ce5b9..0d63bc904 100644 --- a/monkey/infection_monkey/master/propagator.py +++ b/monkey/infection_monkey/master/propagator.py @@ -3,7 +3,7 @@ from queue import Queue from threading import Event, Thread from typing import Dict -from infection_monkey.i_puppet import PingScanData, PortScanData, PortStatus +from infection_monkey.i_puppet import FingerprintData, PingScanData, PortScanData, PortStatus from infection_monkey.model.host import VictimHost from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger from infection_monkey.telemetry.scan_telem import ScanTelem @@ -52,15 +52,33 @@ class Propagator: logger.info("Finished network scan") def _process_scan_results( - self, ip: str, ping_scan_data: PingScanData, port_scan_data: Dict[int, PortScanData] + self, + ip: str, + ping_scan_data: PingScanData, + port_scan_data: Dict[int, PortScanData], + fingerprint_data: Dict[str, FingerprintData], ): victim_host = VictimHost(ip) - has_open_port = False + Propagator._process_ping_scan_results(victim_host, ping_scan_data) + has_open_port = Propagator._process_tcp_scan_results(victim_host, port_scan_data) + Propagator._process_fingerprinter_results(victim_host, fingerprint_data) + + if has_open_port: + self._hosts_to_exploit.put(victim_host) + + self._telemetry_messenger.send_telemetry(ScanTelem(victim_host)) + + @staticmethod + def _process_ping_scan_results(victim_host: VictimHost, ping_scan_data: PingScanData): victim_host.icmp = ping_scan_data.response_received if ping_scan_data.os is not None: victim_host.os["type"] = ping_scan_data.os + @staticmethod + def _process_tcp_scan_results(victim_host: VictimHost, port_scan_data: PortScanData) -> bool: + has_open_port = False + for psd in port_scan_data.values(): if psd.status == PortStatus.OPEN: has_open_port = True @@ -71,10 +89,19 @@ class Propagator: if psd.banner is not None: victim_host.services[psd.service]["banner"] = psd.banner - if has_open_port: - self._hosts_to_exploit.put(victim_host) + return has_open_port - self._telemetry_messenger.send_telemetry(ScanTelem(victim_host)) + @staticmethod + def _process_fingerprinter_results(victim_host: VictimHost, fingerprint_data: FingerprintData): + for fd in fingerprint_data.values(): + if fd.os_type is not None: + victim_host.os["type"] = fd.os_type + + if ("version" not in victim_host.os) and (fd.os_version is not None): + victim_host.os["version"] = fd.os_version + + for service, details in fd.services.items(): + victim_host.services.setdefault(service, {}).update(details) def _exploit_targets(self, scan_thread: Thread, stop: Event): pass 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 b5c97760b..cec779aa5 100644 --- a/monkey/tests/unit_tests/infection_monkey/master/test_propagator.py +++ b/monkey/tests/unit_tests/infection_monkey/master/test_propagator.py @@ -1,8 +1,10 @@ from threading import Event -from infection_monkey.i_puppet import PingScanData, PortScanData, PortStatus +from infection_monkey.i_puppet import FingerprintData, PingScanData, PortScanData, PortStatus from infection_monkey.master import Propagator +empty_fingerprint_data = FingerprintData(None, None, {}) + dot_1_results = ( PingScanData(True, "windows"), { @@ -10,6 +12,11 @@ dot_1_results = ( 445: PortScanData(445, PortStatus.OPEN, "SMB BANNER", "tcp-445"), 3389: PortScanData(3389, PortStatus.OPEN, "", "tcp-3389"), }, + { + "SMBFinger": FingerprintData("windows", "vista", {"tcp-445": {"name": "smb_service_name"}}), + "SSHFinger": empty_fingerprint_data, + "HTTPFinger": empty_fingerprint_data, + }, ) dot_3_results = ( @@ -19,6 +26,20 @@ dot_3_results = ( 443: PortScanData(443, PortStatus.OPEN, "HTTPS BANNER", "tcp-443"), 3389: PortScanData(3389, PortStatus.CLOSED, "", None), }, + { + "SSHFinger": FingerprintData( + "linux", "ubuntu", {"tcp-22": {"name": "SSH", "banner": "SSH BANNER"}} + ), + "HTTPFinger": FingerprintData( + None, + None, + { + "tcp-80": {"name": "http", "data": ("SERVER_HEADERS", False)}, + "tcp-443": {"name": "http", "data": ("SERVER_HEADERS_2", True)}, + }, + ), + "SMBFinger": empty_fingerprint_data, + }, ) dead_host_results = ( @@ -28,21 +49,34 @@ dead_host_results = ( 443: PortScanData(443, PortStatus.CLOSED, None, None), 3389: PortScanData(3389, PortStatus.CLOSED, "", None), }, + {}, ) dot_1_services = { - "tcp-445": {"display_name": "unknown(TCP)", "port": 445, "banner": "SMB BANNER"}, + "tcp-445": { + "name": "smb_service_name", + "display_name": "unknown(TCP)", + "port": 445, + "banner": "SMB BANNER", + }, "tcp-3389": {"display_name": "unknown(TCP)", "port": 3389, "banner": ""}, } dot_3_services = { - "tcp-22": {"display_name": "unknown(TCP)", "port": 22, "banner": "SSH BANNER"}, - "tcp-443": {"display_name": "unknown(TCP)", "port": 443, "banner": "HTTPS BANNER"}, + "tcp-22": {"name": "SSH", "display_name": "unknown(TCP)", "port": 22, "banner": "SSH BANNER"}, + "tcp-80": {"name": "http", "data": ("SERVER_HEADERS", False)}, + "tcp-443": { + "name": "http", + "display_name": "unknown(TCP)", + "port": 443, + "banner": "HTTPS BANNER", + "data": ("SERVER_HEADERS_2", True), + }, } class MockIPScanner: - def scan(self, ips_to_scan, options, results_callback, stop): + def scan(self, ips_to_scan, _, results_callback, stop): for ip in ips_to_scan: if ip.endswith(".1"): results_callback(ip, *dot_1_results) @@ -55,7 +89,10 @@ class MockIPScanner: def test_scan_result_processing(telemetry_messenger_spy): p = Propagator(telemetry_messenger_spy, MockIPScanner()) p.propagate( - {"targets": {"subnet_scan_list": ["10.0.0.1", "10.0.0.2", "10.0.0.3"]}, "network_scan": {}}, + { + "targets": {"subnet_scan_list": ["10.0.0.1", "10.0.0.2", "10.0.0.3"]}, + "network_scan": {}, + }, Event(), ) @@ -68,11 +105,13 @@ def test_scan_result_processing(telemetry_messenger_spy): if ip.endswith(".1"): assert data["service_count"] == 2 assert data["machine"]["os"]["type"] == "windows" + assert data["machine"]["os"]["version"] == "vista" assert data["machine"]["services"] == dot_1_services assert data["machine"]["icmp"] is True elif ip.endswith(".3"): - assert data["service_count"] == 2 + assert data["service_count"] == 3 assert data["machine"]["os"]["type"] == "linux" + assert data["machine"]["os"]["version"] == "ubuntu" assert data["machine"]["services"] == dot_3_services assert data["machine"]["icmp"] is True else: