From ab591fcf4c4696d51ac5c90ae9e632a9ee9b8ca3 Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Wed, 30 Oct 2019 14:26:20 +0200 Subject: [PATCH 01/21] Remove HostScanner and HostFinger to their own files --- monkey/infection_monkey/exploit/smbexec.py | 2 +- .../infection_monkey/exploit/win_ms08_067.py | 9 ++--- monkey/infection_monkey/network/HostFinger.py | 26 ++++++++++++++ .../infection_monkey/network/HostScanner.py | 7 ++++ monkey/infection_monkey/network/__init__.py | 35 ------------------- .../infection_monkey/network/elasticfinger.py | 5 +-- monkey/infection_monkey/network/httpfinger.py | 5 +-- .../network/mssql_fingerprint.py | 5 +-- .../infection_monkey/network/mysqlfinger.py | 5 +-- .../network/network_scanner.py | 3 +- .../infection_monkey/network/ping_scanner.py | 6 ++-- monkey/infection_monkey/network/smbfinger.py | 5 +-- monkey/infection_monkey/network/sshfinger.py | 5 +-- .../infection_monkey/network/tcp_scanner.py | 7 ++-- 14 files changed, 67 insertions(+), 58 deletions(-) create mode 100644 monkey/infection_monkey/network/HostFinger.py create mode 100644 monkey/infection_monkey/network/HostScanner.py diff --git a/monkey/infection_monkey/exploit/smbexec.py b/monkey/infection_monkey/exploit/smbexec.py index 348b6803d..fef8dad05 100644 --- a/monkey/infection_monkey/exploit/smbexec.py +++ b/monkey/infection_monkey/exploit/smbexec.py @@ -7,7 +7,7 @@ from infection_monkey.exploit import HostExploiter from infection_monkey.exploit.tools.helpers import get_target_monkey, get_monkey_depth, build_monkey_commandline from infection_monkey.exploit.tools.smb_tools import SmbTools from infection_monkey.model import MONKEY_CMDLINE_DETACHED_WINDOWS, DROPPER_CMDLINE_DETACHED_WINDOWS -from infection_monkey.network import SMBFinger +from infection_monkey.network.smbfinger import SMBFinger from infection_monkey.network.tools import check_tcp_port from common.utils.exploit_enum import ExploitType from infection_monkey.telemetry.attack.t1035_telem import T1035Telem diff --git a/monkey/infection_monkey/exploit/win_ms08_067.py b/monkey/infection_monkey/exploit/win_ms08_067.py index 7148ba965..1f7f8f12b 100644 --- a/monkey/infection_monkey/exploit/win_ms08_067.py +++ b/monkey/infection_monkey/exploit/win_ms08_067.py @@ -17,7 +17,7 @@ from impacket.dcerpc.v5 import transport from infection_monkey.exploit.tools.helpers import get_target_monkey, get_monkey_depth, build_monkey_commandline from infection_monkey.exploit.tools.smb_tools import SmbTools from infection_monkey.model import DROPPER_CMDLINE_WINDOWS, MONKEY_CMDLINE_WINDOWS -from infection_monkey.network import SMBFinger +from infection_monkey.network.smbfinger import SMBFinger from infection_monkey.network.tools import check_tcp_port from . import HostExploiter @@ -162,11 +162,11 @@ class Ms08_067_Exploiter(HostExploiter): def is_os_supported(self): if self.host.os.get('type') in self._TARGET_OS_TYPE and \ - self.host.os.get('version') in list(self._windows_versions.keys()): + self.host.os.get('version') in list(self._windows_versions.keys()): return True if not self.host.os.get('type') or ( - self.host.os.get('type') in self._TARGET_OS_TYPE and not self.host.os.get('version')): + self.host.os.get('type') in self._TARGET_OS_TYPE and not self.host.os.get('version')): is_smb_open, _ = check_tcp_port(self.host.ip_addr, 445) if is_smb_open: smb_finger = SMBFinger() @@ -234,7 +234,8 @@ class Ms08_067_Exploiter(HostExploiter): # execute the remote dropper in case the path isn't final if remote_full_path.lower() != self._config.dropper_target_path_win_32.lower(): cmdline = DROPPER_CMDLINE_WINDOWS % {'dropper_path': remote_full_path} + \ - build_monkey_commandline(self.host, get_monkey_depth() - 1, self._config.dropper_target_path_win_32) + build_monkey_commandline(self.host, get_monkey_depth() - 1, + self._config.dropper_target_path_win_32) else: cmdline = MONKEY_CMDLINE_WINDOWS % {'monkey_path': remote_full_path} + \ build_monkey_commandline(self.host, get_monkey_depth() - 1) diff --git a/monkey/infection_monkey/network/HostFinger.py b/monkey/infection_monkey/network/HostFinger.py new file mode 100644 index 000000000..e660c8c6c --- /dev/null +++ b/monkey/infection_monkey/network/HostFinger.py @@ -0,0 +1,26 @@ +from abc import ABCMeta, abstractproperty, abstractmethod + +from infection_monkey.config import WormConfiguration + + +class HostFinger(object, metaclass=ABCMeta): + @abstractproperty + def _SCANNED_SERVICE(self): + pass + + def init_service(self, services, service_key, port): + services[service_key] = {} + services[service_key]['display_name'] = self._SCANNED_SERVICE + services[service_key]['port'] = port + + @abstractmethod + def get_host_fingerprint(self, host): + raise NotImplementedError() + + @staticmethod + def should_run(class_name): + """ + Decides if post breach action is enabled in config + :return: True if it needs to be ran, false otherwise + """ + return class_name in WormConfiguration.finger_classes diff --git a/monkey/infection_monkey/network/HostScanner.py b/monkey/infection_monkey/network/HostScanner.py new file mode 100644 index 000000000..f32af1c5e --- /dev/null +++ b/monkey/infection_monkey/network/HostScanner.py @@ -0,0 +1,7 @@ +from abc import ABCMeta, abstractmethod + + +class HostScanner(object, metaclass=ABCMeta): + @abstractmethod + def is_host_alive(self, host): + raise NotImplementedError() diff --git a/monkey/infection_monkey/network/__init__.py b/monkey/infection_monkey/network/__init__.py index fe3e6cb7a..05a457b0c 100644 --- a/monkey/infection_monkey/network/__init__.py +++ b/monkey/infection_monkey/network/__init__.py @@ -1,36 +1 @@ -from abc import ABCMeta, abstractmethod - __author__ = 'itamar' - - -class HostScanner(object, metaclass=ABCMeta): - @abstractmethod - def is_host_alive(self, host): - raise NotImplementedError() - - -class HostFinger(object, metaclass=ABCMeta): - @property - @abstractmethod - def _SCANNED_SERVICE(self): - pass - - def init_service(self, services, service_key, port): - services[service_key] = {} - services[service_key]['display_name'] = self._SCANNED_SERVICE - services[service_key]['port'] = port - - @abstractmethod - def get_host_fingerprint(self, host): - raise NotImplementedError() - - -from infection_monkey.network.ping_scanner import PingScanner -from infection_monkey.network.tcp_scanner import TcpScanner -from infection_monkey.network.smbfinger import SMBFinger -from infection_monkey.network.sshfinger import SSHFinger -from infection_monkey.network.httpfinger import HTTPFinger -from infection_monkey.network.elasticfinger import ElasticFinger -from infection_monkey.network.mysqlfinger import MySQLFinger -from infection_monkey.network.info import local_ips, get_free_tcp_port -from infection_monkey.network.mssql_fingerprint import MSSQLFinger diff --git a/monkey/infection_monkey/network/elasticfinger.py b/monkey/infection_monkey/network/elasticfinger.py index aaac09be2..228d4cdbe 100644 --- a/monkey/infection_monkey/network/elasticfinger.py +++ b/monkey/infection_monkey/network/elasticfinger.py @@ -6,9 +6,10 @@ import requests from requests.exceptions import Timeout, ConnectionError import infection_monkey.config +import infection_monkey.network.HostFinger from common.data.network_consts import ES_SERVICE from infection_monkey.model.host import VictimHost -from infection_monkey.network import HostFinger +import infection_monkey.network ES_PORT = 9200 ES_HTTP_TIMEOUT = 5 @@ -16,7 +17,7 @@ LOG = logging.getLogger(__name__) __author__ = 'danielg' -class ElasticFinger(HostFinger): +class ElasticFinger(infection_monkey.network.HostFinger.HostFinger): """ Fingerprints elastic search clusters, only on port 9200 """ diff --git a/monkey/infection_monkey/network/httpfinger.py b/monkey/infection_monkey/network/httpfinger.py index 30292d99f..0b1cd273e 100644 --- a/monkey/infection_monkey/network/httpfinger.py +++ b/monkey/infection_monkey/network/httpfinger.py @@ -1,12 +1,13 @@ import infection_monkey.config -from infection_monkey.network import HostFinger +import infection_monkey.network +import infection_monkey.network.HostFinger from infection_monkey.model.host import VictimHost import logging LOG = logging.getLogger(__name__) -class HTTPFinger(HostFinger): +class HTTPFinger(infection_monkey.network.HostFinger.HostFinger): """ Goal is to recognise HTTP servers, where what we currently care about is apache. """ diff --git a/monkey/infection_monkey/network/mssql_fingerprint.py b/monkey/infection_monkey/network/mssql_fingerprint.py index e6130732d..4e7d35f26 100644 --- a/monkey/infection_monkey/network/mssql_fingerprint.py +++ b/monkey/infection_monkey/network/mssql_fingerprint.py @@ -2,8 +2,9 @@ import errno import logging import socket +import infection_monkey.network.HostFinger from infection_monkey.model.host import VictimHost -from infection_monkey.network import HostFinger +import infection_monkey.network import infection_monkey.config __author__ = 'Maor Rayzin' @@ -11,7 +12,7 @@ __author__ = 'Maor Rayzin' LOG = logging.getLogger(__name__) -class MSSQLFinger(HostFinger): +class MSSQLFinger(infection_monkey.network.HostFinger.HostFinger): # Class related consts SQL_BROWSER_DEFAULT_PORT = 1434 diff --git a/monkey/infection_monkey/network/mysqlfinger.py b/monkey/infection_monkey/network/mysqlfinger.py index 123f0ae47..50af642b2 100644 --- a/monkey/infection_monkey/network/mysqlfinger.py +++ b/monkey/infection_monkey/network/mysqlfinger.py @@ -2,8 +2,9 @@ import logging import socket import infection_monkey.config +import infection_monkey.network.HostFinger from infection_monkey.model.host import VictimHost -from infection_monkey.network import HostFinger +import infection_monkey.network from infection_monkey.network.tools import struct_unpack_tracker, struct_unpack_tracker_string MYSQL_PORT = 3306 @@ -11,7 +12,7 @@ SQL_SERVICE = 'mysqld-3306' LOG = logging.getLogger(__name__) -class MySQLFinger(HostFinger): +class MySQLFinger(infection_monkey.network.HostFinger.HostFinger): """ Fingerprints mysql databases, only on port 3306 """ diff --git a/monkey/infection_monkey/network/network_scanner.py b/monkey/infection_monkey/network/network_scanner.py index 50fd21b4d..faa5e9a5f 100644 --- a/monkey/infection_monkey/network/network_scanner.py +++ b/monkey/infection_monkey/network/network_scanner.py @@ -6,7 +6,8 @@ from common.network.network_range import NetworkRange from infection_monkey.config import WormConfiguration from infection_monkey.model.victim_host_generator import VictimHostGenerator from infection_monkey.network.info import local_ips, get_interfaces_ranges -from infection_monkey.network import TcpScanner, PingScanner +from infection_monkey.network.tcp_scanner import TcpScanner +from infection_monkey.network.ping_scanner import PingScanner LOG = logging.getLogger(__name__) diff --git a/monkey/infection_monkey/network/ping_scanner.py b/monkey/infection_monkey/network/ping_scanner.py index bf215168e..8838d17d0 100644 --- a/monkey/infection_monkey/network/ping_scanner.py +++ b/monkey/infection_monkey/network/ping_scanner.py @@ -5,8 +5,10 @@ import subprocess import sys import infection_monkey.config +import infection_monkey.network.HostFinger +import infection_monkey.network.HostScanner from infection_monkey.model.host import VictimHost -from infection_monkey.network import HostScanner, HostFinger +import infection_monkey.network __author__ = 'itamar' @@ -19,7 +21,7 @@ WINDOWS_TTL = 128 LOG = logging.getLogger(__name__) -class PingScanner(HostScanner, HostFinger): +class PingScanner(infection_monkey.network.HostScanner.HostScanner, infection_monkey.network.HostFinger.HostFinger): _SCANNED_SERVICE = '' diff --git a/monkey/infection_monkey/network/smbfinger.py b/monkey/infection_monkey/network/smbfinger.py index 8a267e9d1..2e81db4b1 100644 --- a/monkey/infection_monkey/network/smbfinger.py +++ b/monkey/infection_monkey/network/smbfinger.py @@ -3,7 +3,8 @@ import struct import logging from odict import odict -from infection_monkey.network import HostFinger +import infection_monkey.network +import infection_monkey.network.HostFinger from infection_monkey.model.host import VictimHost SMB_PORT = 445 @@ -100,7 +101,7 @@ class SMBSessionFingerData(Packet): self.fields["bcc1"] = struct.pack(" Date: Wed, 30 Oct 2019 14:29:27 +0200 Subject: [PATCH 02/21] Introduce fingerprint manager to avoid having to explictly import classes in configuration. Similar to PBA manager. --- monkey/infection_monkey/config.py | 3 -- monkey/infection_monkey/monkey.py | 6 ++- .../network/fingerprinter_manager.py | 42 +++++++++++++++++++ 3 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 monkey/infection_monkey/network/fingerprinter_manager.py diff --git a/monkey/infection_monkey/config.py b/monkey/infection_monkey/config.py index 6b01761d1..56c221c78 100644 --- a/monkey/infection_monkey/config.py +++ b/monkey/infection_monkey/config.py @@ -33,9 +33,6 @@ class Configuration(object): if self._depth_from_commandline and key == "depth": continue # handle in cases - if key == 'finger_classes': - class_objects = [getattr(network_import, val) for val in value] - setattr(self, key, class_objects) elif key == 'exploiter_classes': class_objects = [getattr(exploit_import, val) for val in value] setattr(self, key, class_objects) diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index 3985c8a2e..200e6bfd4 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -12,6 +12,7 @@ from infection_monkey.utils.environment import is_windows_os from infection_monkey.config import WormConfiguration from infection_monkey.control import ControlClient from infection_monkey.model import DELAY_DELETE_CMD +from infection_monkey.network.fingerprinter_manager import get_fingerprint_instances from infection_monkey.network.firewall import app as firewall from infection_monkey.network.network_scanner import NetworkScanner from infection_monkey.system_info import SystemInfoCollector @@ -145,7 +146,7 @@ class InfectionMonkey(object): self._exploiters = WormConfiguration.exploiter_classes - self._fingerprint = [fingerprint() for fingerprint in WormConfiguration.finger_classes] + self._fingerprint = get_fingerprint_instances() if not self._keep_running or not WormConfiguration.alive: break @@ -182,7 +183,8 @@ class InfectionMonkey(object): if self._default_server: if self._network.on_island(self._default_server): machine.set_default_server(get_interface_to_target(machine.ip_addr) + - (':'+self._default_server_port if self._default_server_port else '')) + ( + ':' + self._default_server_port if self._default_server_port else '')) else: machine.set_default_server(self._default_server) LOG.debug("Default server for machine: %r set to %s" % (machine, machine.default_server)) diff --git a/monkey/infection_monkey/network/fingerprinter_manager.py b/monkey/infection_monkey/network/fingerprinter_manager.py new file mode 100644 index 000000000..5b1005690 --- /dev/null +++ b/monkey/infection_monkey/network/fingerprinter_manager.py @@ -0,0 +1,42 @@ +import importlib +import inspect +import logging +from os.path import dirname, basename, isfile, join +import glob + +from infection_monkey.network.HostFinger import HostFinger + +LOG = logging.getLogger(__name__) + + +def get_fingerprint_files(): + """ + Gets all files under current directory(/actions) + :return: list of all files without .py ending + """ + files = glob.glob(join(dirname(__file__), "*.py")) + return [basename(f)[:-3] for f in files if isfile(f) and not f.endswith('__init__.py')] + + +def get_fingerprint_instances(): + """ + Returns the fingerprint objects according to configuration as a list + :return: A list of HostFinger objects. + """ + fingerprinter_objects = [] + fingerprint_files = get_fingerprint_files() + # Go through all of files + for file in fingerprint_files: + # Import module from that file + module = importlib.import_module(__package__ + '.' + file) + # Get all classes in a module + classes = [m[1] for m in inspect.getmembers(module, inspect.isclass) + if ((m[1].__module__ == module.__name__) and issubclass(m[1], HostFinger))] + # Get object from class + for class_object in classes: + LOG.debug("Checking if should run Fingerprinter {}".format(class_object.__name__)) + if class_object.should_run(class_object.__name__): + fingerprinter = class_object() + fingerprinter_objects.append(fingerprinter) + LOG.debug("Added fingerprinter {} to fingerprint list".format(class_object.__name__)) + return fingerprinter_objects From 1a874762b18ad96f3964a0f0caaee21888648dd5 Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Wed, 30 Oct 2019 14:41:33 +0200 Subject: [PATCH 03/21] Add pyinstaller hook since we are now not importing anything. This is currently messy as there is no plugin directory for fingerprinters --- .../pyinstaller_hooks/hook-infection_monkey.network.py | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.network.py diff --git a/monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.network.py b/monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.network.py new file mode 100644 index 000000000..dbc345780 --- /dev/null +++ b/monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.network.py @@ -0,0 +1,4 @@ +from PyInstaller.utils.hooks import collect_submodules, collect_data_files + +hiddenimports = collect_submodules('infection_monkey.network') +datas = (collect_data_files('infection_monkey.network', include_py_files=True)) From 1df3e30003fd9a28546b185bc0bc06012501fedf Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Wed, 30 Oct 2019 15:07:45 +0200 Subject: [PATCH 04/21] Refactor plugin loading into independent module --- .../network/fingerprinter_manager.py | 33 +-------------- monkey/infection_monkey/utils/load_plugins.py | 41 +++++++++++++++++++ 2 files changed, 43 insertions(+), 31 deletions(-) create mode 100644 monkey/infection_monkey/utils/load_plugins.py diff --git a/monkey/infection_monkey/network/fingerprinter_manager.py b/monkey/infection_monkey/network/fingerprinter_manager.py index 5b1005690..5eaeeafdb 100644 --- a/monkey/infection_monkey/network/fingerprinter_manager.py +++ b/monkey/infection_monkey/network/fingerprinter_manager.py @@ -1,42 +1,13 @@ -import importlib -import inspect import logging -from os.path import dirname, basename, isfile, join -import glob - +from infection_monkey.utils.load_plugins import get_instances from infection_monkey.network.HostFinger import HostFinger LOG = logging.getLogger(__name__) -def get_fingerprint_files(): - """ - Gets all files under current directory(/actions) - :return: list of all files without .py ending - """ - files = glob.glob(join(dirname(__file__), "*.py")) - return [basename(f)[:-3] for f in files if isfile(f) and not f.endswith('__init__.py')] - - def get_fingerprint_instances(): """ Returns the fingerprint objects according to configuration as a list :return: A list of HostFinger objects. """ - fingerprinter_objects = [] - fingerprint_files = get_fingerprint_files() - # Go through all of files - for file in fingerprint_files: - # Import module from that file - module = importlib.import_module(__package__ + '.' + file) - # Get all classes in a module - classes = [m[1] for m in inspect.getmembers(module, inspect.isclass) - if ((m[1].__module__ == module.__name__) and issubclass(m[1], HostFinger))] - # Get object from class - for class_object in classes: - LOG.debug("Checking if should run Fingerprinter {}".format(class_object.__name__)) - if class_object.should_run(class_object.__name__): - fingerprinter = class_object() - fingerprinter_objects.append(fingerprinter) - LOG.debug("Added fingerprinter {} to fingerprint list".format(class_object.__name__)) - return fingerprinter_objects + return get_instances(__package__, __file__, HostFinger) diff --git a/monkey/infection_monkey/utils/load_plugins.py b/monkey/infection_monkey/utils/load_plugins.py new file mode 100644 index 000000000..bb075e5e8 --- /dev/null +++ b/monkey/infection_monkey/utils/load_plugins.py @@ -0,0 +1,41 @@ +import importlib +import inspect +import logging +from os.path import dirname, basename, isfile, join +import glob + +LOG = logging.getLogger(__name__) + + +def _get_candidate_files(base_package_file): + """ + Gets all files under current directory(/actions) + :return: list of all files without .py ending + """ + files = glob.glob(join(dirname(base_package_file), "*.py")) + return [basename(f)[:-3] for f in files if isfile(f) and not f.endswith('__init__.py')] + + +def get_instances(base_package_name, base_package_file, parent_class): + """ + Returns the parent_class type objects from base_package_spec according to configuration + :return: A list of parent_class objects. + """ + objects = [] + candidate_files = _get_candidate_files(base_package_file) + # Go through all of files + for file in candidate_files: + # Import module from that file + module = importlib.import_module(base_package_name + '.' + file) + # Get all classes in a module + # m[1] because return object is (name,class) + classes = [m[1] for m in inspect.getmembers(module, inspect.isclass) + if ((m[1].__module__ == module.__name__) and issubclass(m[1], parent_class))] + # Get object from class + for class_object in classes: + LOG.debug("Checking if should run object {}".format(class_object.__name__)) + if class_object.should_run(class_object.__name__): + instance = class_object() + objects.append(instance) + LOG.debug("Added {} to list".format(class_object.__name__)) + return objects From 9c40b4a022f9d6663fb4ebb883f4011f4e471927 Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Wed, 30 Oct 2019 15:14:27 +0200 Subject: [PATCH 05/21] Refactor PBA to use generic plugin --- .../post_breach/actions/__init__.py | 11 -------- .../post_breach/post_breach_handler.py | 27 +++++-------------- 2 files changed, 6 insertions(+), 32 deletions(-) diff --git a/monkey/infection_monkey/post_breach/actions/__init__.py b/monkey/infection_monkey/post_breach/actions/__init__.py index 17007f1e6..e69de29bb 100644 --- a/monkey/infection_monkey/post_breach/actions/__init__.py +++ b/monkey/infection_monkey/post_breach/actions/__init__.py @@ -1,11 +0,0 @@ -from os.path import dirname, basename, isfile, join -import glob - - -def get_pba_files(): - """ - Gets all files under current directory(/actions) - :return: list of all files without .py ending - """ - files = glob.glob(join(dirname(__file__), "*.py")) - return [basename(f)[:-3] for f in files if isfile(f) and not f.endswith('__init__.py')] diff --git a/monkey/infection_monkey/post_breach/post_breach_handler.py b/monkey/infection_monkey/post_breach/post_breach_handler.py index b5dfa93c7..a98439d99 100644 --- a/monkey/infection_monkey/post_breach/post_breach_handler.py +++ b/monkey/infection_monkey/post_breach/post_breach_handler.py @@ -1,9 +1,8 @@ import logging -import inspect -import importlib -from infection_monkey.post_breach.pba import PBA -from infection_monkey.post_breach.actions import get_pba_files from infection_monkey.utils.environment import is_windows_os +from infection_monkey.utils.load_plugins import get_instances +from infection_monkey.post_breach.pba import PBA +import infection_monkey.post_breach.actions LOG = logging.getLogger(__name__) @@ -16,6 +15,7 @@ class PostBreach(object): """ This class handles post breach actions execution """ + def __init__(self): self.os_is_linux = not is_windows_os() self.pba_list = self.config_to_pba_list() @@ -38,20 +38,5 @@ class PostBreach(object): Passes config to each post breach action class and aggregates results into a list. :return: A list of PBA objects. """ - pba_list = [] - pba_files = get_pba_files() - # Go through all of files in ./actions - for pba_file in pba_files: - # Import module from that file - module = importlib.import_module(PATH_TO_ACTIONS + pba_file) - # Get all classes in a module - pba_classes = [m[1] for m in inspect.getmembers(module, inspect.isclass) - if ((m[1].__module__ == module.__name__) and issubclass(m[1], PBA))] - # Get post breach action object from class - for pba_class in pba_classes: - LOG.debug("Checking if should run PBA {}".format(pba_class.__name__)) - if pba_class.should_run(pba_class.__name__): - pba = pba_class() - pba_list.append(pba) - LOG.debug("Added PBA {} to PBA list".format(pba_class.__name__)) - return pba_list + return get_instances(infection_monkey.post_breach.actions.__package__, + infection_monkey.post_breach.actions.__file__, PBA) From 37704e3eeb957fb6b35e0a8cf6b5dd2fd14c5460 Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Wed, 30 Oct 2019 15:23:37 +0200 Subject: [PATCH 06/21] Slightly cleaner usage of importlib, making sure we import the correct module --- monkey/infection_monkey/utils/load_plugins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/infection_monkey/utils/load_plugins.py b/monkey/infection_monkey/utils/load_plugins.py index bb075e5e8..8516d8a02 100644 --- a/monkey/infection_monkey/utils/load_plugins.py +++ b/monkey/infection_monkey/utils/load_plugins.py @@ -26,7 +26,7 @@ def get_instances(base_package_name, base_package_file, parent_class): # Go through all of files for file in candidate_files: # Import module from that file - module = importlib.import_module(base_package_name + '.' + file) + module = importlib.import_module('.' + file, base_package_name) # Get all classes in a module # m[1] because return object is (name,class) classes = [m[1] for m in inspect.getmembers(module, inspect.isclass) From 0044eb393b0a3fad93a922399f9f45cada3d98f7 Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Sun, 3 Nov 2019 14:51:33 -0500 Subject: [PATCH 07/21] Formatting fail --- monkey/infection_monkey/monkey.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index 200e6bfd4..0fa1fa4fa 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -182,9 +182,8 @@ class InfectionMonkey(object): monkey_tunnel.set_tunnel_for_host(machine) if self._default_server: if self._network.on_island(self._default_server): - machine.set_default_server(get_interface_to_target(machine.ip_addr) + - ( - ':' + self._default_server_port if self._default_server_port else '')) + machine.set_default_server(get_interface_to_target(machine.ip_addr) + ( + ':' + self._default_server_port if self._default_server_port else '')) else: machine.set_default_server(self._default_server) LOG.debug("Default server for machine: %r set to %s" % (machine, machine.default_server)) From 029c3bb24d4571155eaf4db38550a0738033b7b7 Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Sun, 3 Nov 2019 15:01:26 -0500 Subject: [PATCH 08/21] Change property names. Add type hints --- monkey/infection_monkey/network/HostFinger.py | 11 ++++------- monkey/infection_monkey/network/HostScanner.py | 3 ++- .../infection_monkey/network/fingerprinter_manager.py | 3 ++- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/monkey/infection_monkey/network/HostFinger.py b/monkey/infection_monkey/network/HostFinger.py index e660c8c6c..1eecabe6f 100644 --- a/monkey/infection_monkey/network/HostFinger.py +++ b/monkey/infection_monkey/network/HostFinger.py @@ -3,8 +3,9 @@ from abc import ABCMeta, abstractproperty, abstractmethod from infection_monkey.config import WormConfiguration -class HostFinger(object, metaclass=ABCMeta): - @abstractproperty +class HostFinger(metaclass=ABCMeta): + @property + @abstractmethod def _SCANNED_SERVICE(self): pass @@ -18,9 +19,5 @@ class HostFinger(object, metaclass=ABCMeta): raise NotImplementedError() @staticmethod - def should_run(class_name): - """ - Decides if post breach action is enabled in config - :return: True if it needs to be ran, false otherwise - """ + def should_run(class_name: str) -> bool: return class_name in WormConfiguration.finger_classes diff --git a/monkey/infection_monkey/network/HostScanner.py b/monkey/infection_monkey/network/HostScanner.py index f32af1c5e..c6e730c3f 100644 --- a/monkey/infection_monkey/network/HostScanner.py +++ b/monkey/infection_monkey/network/HostScanner.py @@ -1,7 +1,8 @@ from abc import ABCMeta, abstractmethod -class HostScanner(object, metaclass=ABCMeta): +class HostScanner(metaclass=ABCMeta): + @property @abstractmethod def is_host_alive(self, host): raise NotImplementedError() diff --git a/monkey/infection_monkey/network/fingerprinter_manager.py b/monkey/infection_monkey/network/fingerprinter_manager.py index 5eaeeafdb..9a6c83082 100644 --- a/monkey/infection_monkey/network/fingerprinter_manager.py +++ b/monkey/infection_monkey/network/fingerprinter_manager.py @@ -1,11 +1,12 @@ import logging +from typing import Sequence from infection_monkey.utils.load_plugins import get_instances from infection_monkey.network.HostFinger import HostFinger LOG = logging.getLogger(__name__) -def get_fingerprint_instances(): +def get_fingerprint_instances() -> Sequence[HostFinger]: """ Returns the fingerprint objects according to configuration as a list :return: A list of HostFinger objects. From cd716eb1c8c89facf6f06722070bc09348a7a5ee Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Sun, 3 Nov 2019 15:03:00 -0500 Subject: [PATCH 09/21] Remove unused imports. Removed bad asserts --- monkey/infection_monkey/network/elasticfinger.py | 3 --- monkey/infection_monkey/network/httpfinger.py | 3 --- monkey/infection_monkey/network/mssql_fingerprint.py | 3 --- monkey/infection_monkey/network/mysqlfinger.py | 3 --- monkey/infection_monkey/network/ping_scanner.py | 4 ---- monkey/infection_monkey/network/smbfinger.py | 3 --- monkey/infection_monkey/network/sshfinger.py | 3 --- monkey/infection_monkey/network/tcp_scanner.py | 1 - 8 files changed, 23 deletions(-) diff --git a/monkey/infection_monkey/network/elasticfinger.py b/monkey/infection_monkey/network/elasticfinger.py index 228d4cdbe..41eabd18a 100644 --- a/monkey/infection_monkey/network/elasticfinger.py +++ b/monkey/infection_monkey/network/elasticfinger.py @@ -8,8 +8,6 @@ from requests.exceptions import Timeout, ConnectionError import infection_monkey.config import infection_monkey.network.HostFinger from common.data.network_consts import ES_SERVICE -from infection_monkey.model.host import VictimHost -import infection_monkey.network ES_PORT = 9200 ES_HTTP_TIMEOUT = 5 @@ -32,7 +30,6 @@ class ElasticFinger(infection_monkey.network.HostFinger.HostFinger): :param host: :return: Success/failure, data is saved in the host struct """ - assert isinstance(host, VictimHost) try: url = 'http://%s:%s/' % (host.ip_addr, ES_PORT) with closing(requests.get(url, timeout=ES_HTTP_TIMEOUT)) as req: diff --git a/monkey/infection_monkey/network/httpfinger.py b/monkey/infection_monkey/network/httpfinger.py index 0b1cd273e..ca5742c07 100644 --- a/monkey/infection_monkey/network/httpfinger.py +++ b/monkey/infection_monkey/network/httpfinger.py @@ -1,7 +1,5 @@ import infection_monkey.config -import infection_monkey.network import infection_monkey.network.HostFinger -from infection_monkey.model.host import VictimHost import logging LOG = logging.getLogger(__name__) @@ -22,7 +20,6 @@ class HTTPFinger(infection_monkey.network.HostFinger.HostFinger): pass def get_host_fingerprint(self, host): - assert isinstance(host, VictimHost) from requests import head from requests.exceptions import Timeout, ConnectionError from contextlib import closing diff --git a/monkey/infection_monkey/network/mssql_fingerprint.py b/monkey/infection_monkey/network/mssql_fingerprint.py index 4e7d35f26..87d08541d 100644 --- a/monkey/infection_monkey/network/mssql_fingerprint.py +++ b/monkey/infection_monkey/network/mssql_fingerprint.py @@ -3,8 +3,6 @@ import logging import socket import infection_monkey.network.HostFinger -from infection_monkey.model.host import VictimHost -import infection_monkey.network import infection_monkey.config __author__ = 'Maor Rayzin' @@ -32,7 +30,6 @@ class MSSQLFinger(infection_monkey.network.HostFinger.HostFinger): Discovered server information written to the Host info struct. True if success, False otherwise. """ - assert isinstance(host, VictimHost) # Create a UDP socket and sets a timeout sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) diff --git a/monkey/infection_monkey/network/mysqlfinger.py b/monkey/infection_monkey/network/mysqlfinger.py index 50af642b2..eef675507 100644 --- a/monkey/infection_monkey/network/mysqlfinger.py +++ b/monkey/infection_monkey/network/mysqlfinger.py @@ -3,8 +3,6 @@ import socket import infection_monkey.config import infection_monkey.network.HostFinger -from infection_monkey.model.host import VictimHost -import infection_monkey.network from infection_monkey.network.tools import struct_unpack_tracker, struct_unpack_tracker_string MYSQL_PORT = 3306 @@ -29,7 +27,6 @@ class MySQLFinger(infection_monkey.network.HostFinger.HostFinger): :param host: :return: Success/failure, data is saved in the host struct """ - assert isinstance(host, VictimHost) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(self.SOCKET_TIMEOUT) diff --git a/monkey/infection_monkey/network/ping_scanner.py b/monkey/infection_monkey/network/ping_scanner.py index 8838d17d0..899cc64b6 100644 --- a/monkey/infection_monkey/network/ping_scanner.py +++ b/monkey/infection_monkey/network/ping_scanner.py @@ -7,8 +7,6 @@ import sys import infection_monkey.config import infection_monkey.network.HostFinger import infection_monkey.network.HostScanner -from infection_monkey.model.host import VictimHost -import infection_monkey.network __author__ = 'itamar' @@ -31,7 +29,6 @@ class PingScanner(infection_monkey.network.HostScanner.HostScanner, infection_mo self._ttl_regex = re.compile(TTL_REGEX_STR, re.IGNORECASE) def is_host_alive(self, host): - assert isinstance(host, VictimHost) timeout = self._config.ping_scan_timeout if not "win32" == sys.platform: @@ -45,7 +42,6 @@ class PingScanner(infection_monkey.network.HostScanner.HostScanner, infection_mo stderr=self._devnull) def get_host_fingerprint(self, host): - assert isinstance(host, VictimHost) timeout = self._config.ping_scan_timeout if not "win32" == sys.platform: diff --git a/monkey/infection_monkey/network/smbfinger.py b/monkey/infection_monkey/network/smbfinger.py index 2e81db4b1..529c1e91a 100644 --- a/monkey/infection_monkey/network/smbfinger.py +++ b/monkey/infection_monkey/network/smbfinger.py @@ -3,9 +3,7 @@ import struct import logging from odict import odict -import infection_monkey.network import infection_monkey.network.HostFinger -from infection_monkey.model.host import VictimHost SMB_PORT = 445 SMB_SERVICE = 'tcp-445' @@ -109,7 +107,6 @@ class SMBFinger(infection_monkey.network.HostFinger.HostFinger): self._config = WormConfiguration def get_host_fingerprint(self, host): - assert isinstance(host, VictimHost) try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) diff --git a/monkey/infection_monkey/network/sshfinger.py b/monkey/infection_monkey/network/sshfinger.py index bc1484de3..cd1cd4aaf 100644 --- a/monkey/infection_monkey/network/sshfinger.py +++ b/monkey/infection_monkey/network/sshfinger.py @@ -2,8 +2,6 @@ import re import infection_monkey.config import infection_monkey.network.HostFinger -from infection_monkey.model.host import VictimHost -import infection_monkey.network from infection_monkey.network.tools import check_tcp_port SSH_PORT = 22 @@ -35,7 +33,6 @@ class SSHFinger(infection_monkey.network.HostFinger.HostFinger): break def get_host_fingerprint(self, host): - assert isinstance(host, VictimHost) for name, data in list(host.services.items()): banner = data.get('banner', '') diff --git a/monkey/infection_monkey/network/tcp_scanner.py b/monkey/infection_monkey/network/tcp_scanner.py index d50100c2b..019417eb0 100644 --- a/monkey/infection_monkey/network/tcp_scanner.py +++ b/monkey/infection_monkey/network/tcp_scanner.py @@ -2,7 +2,6 @@ from itertools import zip_longest from random import shuffle import infection_monkey.config -import infection_monkey.network import infection_monkey.network.HostFinger import infection_monkey.network.HostScanner from infection_monkey.network.tools import check_tcp_ports, tcp_port_to_service From 997125eaddc531159936684a8069689da6c8c8c1 Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Tue, 5 Nov 2019 21:01:01 +0200 Subject: [PATCH 10/21] Revert longish import --- monkey/infection_monkey/network/elasticfinger.py | 4 ++-- monkey/infection_monkey/network/httpfinger.py | 4 ++-- monkey/infection_monkey/network/mssql_fingerprint.py | 4 ++-- monkey/infection_monkey/network/mysqlfinger.py | 4 ++-- monkey/infection_monkey/network/ping_scanner.py | 4 ++-- monkey/infection_monkey/network/smbfinger.py | 4 ++-- monkey/infection_monkey/network/sshfinger.py | 4 ++-- monkey/infection_monkey/network/tcp_scanner.py | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/monkey/infection_monkey/network/elasticfinger.py b/monkey/infection_monkey/network/elasticfinger.py index 41eabd18a..790afa47d 100644 --- a/monkey/infection_monkey/network/elasticfinger.py +++ b/monkey/infection_monkey/network/elasticfinger.py @@ -6,7 +6,7 @@ import requests from requests.exceptions import Timeout, ConnectionError import infection_monkey.config -import infection_monkey.network.HostFinger +from infection_monkey.network.HostFinger import HostFinger from common.data.network_consts import ES_SERVICE ES_PORT = 9200 @@ -15,7 +15,7 @@ LOG = logging.getLogger(__name__) __author__ = 'danielg' -class ElasticFinger(infection_monkey.network.HostFinger.HostFinger): +class ElasticFinger(HostFinger): """ Fingerprints elastic search clusters, only on port 9200 """ diff --git a/monkey/infection_monkey/network/httpfinger.py b/monkey/infection_monkey/network/httpfinger.py index ca5742c07..305a0b6e9 100644 --- a/monkey/infection_monkey/network/httpfinger.py +++ b/monkey/infection_monkey/network/httpfinger.py @@ -1,11 +1,11 @@ import infection_monkey.config -import infection_monkey.network.HostFinger +from infection_monkey.network.HostFinger import HostFinger import logging LOG = logging.getLogger(__name__) -class HTTPFinger(infection_monkey.network.HostFinger.HostFinger): +class HTTPFinger(HostFinger): """ Goal is to recognise HTTP servers, where what we currently care about is apache. """ diff --git a/monkey/infection_monkey/network/mssql_fingerprint.py b/monkey/infection_monkey/network/mssql_fingerprint.py index 87d08541d..568de1528 100644 --- a/monkey/infection_monkey/network/mssql_fingerprint.py +++ b/monkey/infection_monkey/network/mssql_fingerprint.py @@ -2,7 +2,7 @@ import errno import logging import socket -import infection_monkey.network.HostFinger +from infection_monkey.network.HostFinger import HostFinger import infection_monkey.config __author__ = 'Maor Rayzin' @@ -10,7 +10,7 @@ __author__ = 'Maor Rayzin' LOG = logging.getLogger(__name__) -class MSSQLFinger(infection_monkey.network.HostFinger.HostFinger): +class MSSQLFinger(HostFinger): # Class related consts SQL_BROWSER_DEFAULT_PORT = 1434 diff --git a/monkey/infection_monkey/network/mysqlfinger.py b/monkey/infection_monkey/network/mysqlfinger.py index eef675507..e996c9ed6 100644 --- a/monkey/infection_monkey/network/mysqlfinger.py +++ b/monkey/infection_monkey/network/mysqlfinger.py @@ -2,7 +2,7 @@ import logging import socket import infection_monkey.config -import infection_monkey.network.HostFinger +from infection_monkey.network.HostFinger import HostFinger from infection_monkey.network.tools import struct_unpack_tracker, struct_unpack_tracker_string MYSQL_PORT = 3306 @@ -10,7 +10,7 @@ SQL_SERVICE = 'mysqld-3306' LOG = logging.getLogger(__name__) -class MySQLFinger(infection_monkey.network.HostFinger.HostFinger): +class MySQLFinger(HostFinger): """ Fingerprints mysql databases, only on port 3306 """ diff --git a/monkey/infection_monkey/network/ping_scanner.py b/monkey/infection_monkey/network/ping_scanner.py index 899cc64b6..0461da05f 100644 --- a/monkey/infection_monkey/network/ping_scanner.py +++ b/monkey/infection_monkey/network/ping_scanner.py @@ -5,7 +5,7 @@ import subprocess import sys import infection_monkey.config -import infection_monkey.network.HostFinger +from infection_monkey.network.HostFinger import HostFinger import infection_monkey.network.HostScanner __author__ = 'itamar' @@ -19,7 +19,7 @@ WINDOWS_TTL = 128 LOG = logging.getLogger(__name__) -class PingScanner(infection_monkey.network.HostScanner.HostScanner, infection_monkey.network.HostFinger.HostFinger): +class PingScanner(infection_monkey.network.HostScanner.HostScanner, HostFinger): _SCANNED_SERVICE = '' diff --git a/monkey/infection_monkey/network/smbfinger.py b/monkey/infection_monkey/network/smbfinger.py index 529c1e91a..a13857a4a 100644 --- a/monkey/infection_monkey/network/smbfinger.py +++ b/monkey/infection_monkey/network/smbfinger.py @@ -3,7 +3,7 @@ import struct import logging from odict import odict -import infection_monkey.network.HostFinger +from infection_monkey.network.HostFinger import HostFinger SMB_PORT = 445 SMB_SERVICE = 'tcp-445' @@ -99,7 +99,7 @@ class SMBSessionFingerData(Packet): self.fields["bcc1"] = struct.pack(" Date: Tue, 5 Nov 2019 21:04:17 +0200 Subject: [PATCH 11/21] Rename fingerprinter_manager to collector --- monkey/infection_monkey/monkey.py | 2 +- .../{fingerprinter_manager.py => fingerprinters_collector.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename monkey/infection_monkey/network/{fingerprinter_manager.py => fingerprinters_collector.py} (100%) diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index 0fa1fa4fa..6d3941ddc 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -12,7 +12,7 @@ from infection_monkey.utils.environment import is_windows_os from infection_monkey.config import WormConfiguration from infection_monkey.control import ControlClient from infection_monkey.model import DELAY_DELETE_CMD -from infection_monkey.network.fingerprinter_manager import get_fingerprint_instances +from infection_monkey.network.fingerprinters_collector import get_fingerprint_instances from infection_monkey.network.firewall import app as firewall from infection_monkey.network.network_scanner import NetworkScanner from infection_monkey.system_info import SystemInfoCollector diff --git a/monkey/infection_monkey/network/fingerprinter_manager.py b/monkey/infection_monkey/network/fingerprinters_collector.py similarity index 100% rename from monkey/infection_monkey/network/fingerprinter_manager.py rename to monkey/infection_monkey/network/fingerprinters_collector.py From c6216325b6417b40354f88cb3fefe80526599f1d Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Tue, 5 Nov 2019 21:04:28 +0200 Subject: [PATCH 12/21] Removed bad documentation --- monkey/infection_monkey/utils/load_plugins.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/monkey/infection_monkey/utils/load_plugins.py b/monkey/infection_monkey/utils/load_plugins.py index 8516d8a02..4cf676e5f 100644 --- a/monkey/infection_monkey/utils/load_plugins.py +++ b/monkey/infection_monkey/utils/load_plugins.py @@ -8,10 +8,6 @@ LOG = logging.getLogger(__name__) def _get_candidate_files(base_package_file): - """ - Gets all files under current directory(/actions) - :return: list of all files without .py ending - """ files = glob.glob(join(dirname(base_package_file), "*.py")) return [basename(f)[:-3] for f in files if isfile(f) and not f.endswith('__init__.py')] @@ -23,6 +19,7 @@ def get_instances(base_package_name, base_package_file, parent_class): """ objects = [] candidate_files = _get_candidate_files(base_package_file) + LOG.info("looking for classes of type {} in {}".format(parent_class.__name__, base_package_name)) # Go through all of files for file in candidate_files: # Import module from that file From 3c194e1eb8dcc9e2fae1bab7fa9307b888072b53 Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Tue, 5 Nov 2019 21:21:44 +0200 Subject: [PATCH 13/21] Add TODO doc --- monkey/infection_monkey/network/fingerprinters_collector.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/monkey/infection_monkey/network/fingerprinters_collector.py b/monkey/infection_monkey/network/fingerprinters_collector.py index 9a6c83082..a823d7bfb 100644 --- a/monkey/infection_monkey/network/fingerprinters_collector.py +++ b/monkey/infection_monkey/network/fingerprinters_collector.py @@ -11,4 +11,7 @@ def get_fingerprint_instances() -> Sequence[HostFinger]: Returns the fingerprint objects according to configuration as a list :return: A list of HostFinger objects. """ + # note this currently assumes we're in the same package as the fingerprinters + # if this changes, this file should be updated + # like when they move into a network plugins folder return get_instances(__package__, __file__, HostFinger) From 2ab885b9b1705318475119b34ffd07046fb0d605 Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Thu, 7 Nov 2019 15:49:18 +0200 Subject: [PATCH 14/21] Create concept of Plugin and have all current plugins (fingerprinters/PBAs) inherit from it. Move plugin manager to their own package. (prep for tests) --- monkey/infection_monkey/network/HostFinger.py | 3 ++- .../infection_monkey/network/fingerprinters_collector.py | 2 +- monkey/infection_monkey/post_breach/pba.py | 3 ++- .../infection_monkey/post_breach/post_breach_handler.py | 2 +- monkey/infection_monkey/utils/plugins/__init__.py | 0 .../infection_monkey/utils/{ => plugins}/load_plugins.py | 8 ++++++-- monkey/infection_monkey/utils/plugins/plugin.py | 9 +++++++++ 7 files changed, 21 insertions(+), 6 deletions(-) create mode 100644 monkey/infection_monkey/utils/plugins/__init__.py rename monkey/infection_monkey/utils/{ => plugins}/load_plugins.py (87%) create mode 100644 monkey/infection_monkey/utils/plugins/plugin.py diff --git a/monkey/infection_monkey/network/HostFinger.py b/monkey/infection_monkey/network/HostFinger.py index 1eecabe6f..22e5b0698 100644 --- a/monkey/infection_monkey/network/HostFinger.py +++ b/monkey/infection_monkey/network/HostFinger.py @@ -1,9 +1,10 @@ from abc import ABCMeta, abstractproperty, abstractmethod from infection_monkey.config import WormConfiguration +from infection_monkey.utils.plugins.plugin import Plugin -class HostFinger(metaclass=ABCMeta): +class HostFinger(Plugin, metaclass=ABCMeta): @property @abstractmethod def _SCANNED_SERVICE(self): diff --git a/monkey/infection_monkey/network/fingerprinters_collector.py b/monkey/infection_monkey/network/fingerprinters_collector.py index a823d7bfb..a68e00edf 100644 --- a/monkey/infection_monkey/network/fingerprinters_collector.py +++ b/monkey/infection_monkey/network/fingerprinters_collector.py @@ -1,6 +1,6 @@ import logging from typing import Sequence -from infection_monkey.utils.load_plugins import get_instances +from infection_monkey.utils.plugins.load_plugins import get_instances from infection_monkey.network.HostFinger import HostFinger LOG = logging.getLogger(__name__) diff --git a/monkey/infection_monkey/post_breach/pba.py b/monkey/infection_monkey/post_breach/pba.py index 57bf0aaf7..1bb73b604 100644 --- a/monkey/infection_monkey/post_breach/pba.py +++ b/monkey/infection_monkey/post_breach/pba.py @@ -6,6 +6,7 @@ from infection_monkey.telemetry.post_breach_telem import PostBreachTelem from infection_monkey.utils.environment import is_windows_os from infection_monkey.config import WormConfiguration from infection_monkey.telemetry.attack.t1064_telem import T1064Telem +from infection_monkey.utils.plugins.plugin import Plugin LOG = logging.getLogger(__name__) @@ -14,7 +15,7 @@ __author__ = 'VakarisZ' EXECUTION_WITHOUT_OUTPUT = "(PBA execution produced no output)" -class PBA(object): +class PBA(Plugin): """ Post breach action object. Can be extended to support more than command execution on target machine. """ diff --git a/monkey/infection_monkey/post_breach/post_breach_handler.py b/monkey/infection_monkey/post_breach/post_breach_handler.py index a98439d99..e3c81cd23 100644 --- a/monkey/infection_monkey/post_breach/post_breach_handler.py +++ b/monkey/infection_monkey/post_breach/post_breach_handler.py @@ -1,6 +1,6 @@ import logging from infection_monkey.utils.environment import is_windows_os -from infection_monkey.utils.load_plugins import get_instances +from infection_monkey.utils.plugins.load_plugins import get_instances from infection_monkey.post_breach.pba import PBA import infection_monkey.post_breach.actions diff --git a/monkey/infection_monkey/utils/plugins/__init__.py b/monkey/infection_monkey/utils/plugins/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/infection_monkey/utils/load_plugins.py b/monkey/infection_monkey/utils/plugins/load_plugins.py similarity index 87% rename from monkey/infection_monkey/utils/load_plugins.py rename to monkey/infection_monkey/utils/plugins/load_plugins.py index 4cf676e5f..9a4faf771 100644 --- a/monkey/infection_monkey/utils/load_plugins.py +++ b/monkey/infection_monkey/utils/plugins/load_plugins.py @@ -4,6 +4,8 @@ import logging from os.path import dirname, basename, isfile, join import glob +from infection_monkey.utils.plugins.plugin import Plugin + LOG = logging.getLogger(__name__) @@ -12,9 +14,11 @@ def _get_candidate_files(base_package_file): return [basename(f)[:-3] for f in files if isfile(f) and not f.endswith('__init__.py')] -def get_instances(base_package_name, base_package_file, parent_class): +def get_instances(base_package_name, base_package_file, parent_class: Plugin): """ - Returns the parent_class type objects from base_package_spec according to configuration + Returns the parent_class type objects from base_package_spec. + parent_class must be a class object that inherits from Plugin + base_package name and file must refer to the same package otherwise bad results :return: A list of parent_class objects. """ objects = [] diff --git a/monkey/infection_monkey/utils/plugins/plugin.py b/monkey/infection_monkey/utils/plugins/plugin.py new file mode 100644 index 000000000..ca122f70d --- /dev/null +++ b/monkey/infection_monkey/utils/plugins/plugin.py @@ -0,0 +1,9 @@ +from abc import ABCMeta, abstractmethod + + +class Plugin(metaclass=ABCMeta): + + @staticmethod + @abstractmethod + def should_run(class_name: str) -> bool: + raise NotImplementedError() From 4e7269b4dfb537048ee4ca0a988d8b385efdf51b Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Sat, 9 Nov 2019 10:21:48 +0200 Subject: [PATCH 15/21] Exception handling whe building objects --- monkey/infection_monkey/utils/plugins/load_plugins.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/monkey/infection_monkey/utils/plugins/load_plugins.py b/monkey/infection_monkey/utils/plugins/load_plugins.py index 9a4faf771..0f39e70aa 100644 --- a/monkey/infection_monkey/utils/plugins/load_plugins.py +++ b/monkey/infection_monkey/utils/plugins/load_plugins.py @@ -35,8 +35,11 @@ def get_instances(base_package_name, base_package_file, parent_class: Plugin): # Get object from class for class_object in classes: LOG.debug("Checking if should run object {}".format(class_object.__name__)) - if class_object.should_run(class_object.__name__): - instance = class_object() - objects.append(instance) - LOG.debug("Added {} to list".format(class_object.__name__)) + try: + if class_object.should_run(class_object.__name__): + instance = class_object() + objects.append(instance) + LOG.debug("Added {} to list".format(class_object.__name__)) + except Exception as e: + LOG.warning("Exception {} when checking if {} should run".format(str(e), class_object.__name__)) return objects From 87cedacb7d4bace8fb1f4d10955dcf2680fc0dd5 Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Sat, 9 Nov 2019 21:20:10 +0200 Subject: [PATCH 16/21] Really basic test harness for plugins --- .../utils/plugins/load_plugins_test.py | 21 +++++++++++++++++++ .../plugins/plugins_testcases/__init__.py | 0 .../plugins_testcases/plugin_bad_import.py | 1 + .../plugins_testcases/plugin_bad_init.py | 11 ++++++++++ .../plugins_testcases/plugin_bad_parent.py | 4 ++++ .../plugins_testcases/plugin_test_dontrun.py | 7 +++++++ .../plugins_testcases/plugin_test_run.py | 7 +++++++ 7 files changed, 51 insertions(+) create mode 100644 monkey/infection_monkey/utils/plugins/load_plugins_test.py create mode 100644 monkey/infection_monkey/utils/plugins/plugins_testcases/__init__.py create mode 100644 monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_bad_import.py create mode 100644 monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_bad_init.py create mode 100644 monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_bad_parent.py create mode 100644 monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_test_dontrun.py create mode 100644 monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_test_run.py diff --git a/monkey/infection_monkey/utils/plugins/load_plugins_test.py b/monkey/infection_monkey/utils/plugins/load_plugins_test.py new file mode 100644 index 000000000..cb77f5d9d --- /dev/null +++ b/monkey/infection_monkey/utils/plugins/load_plugins_test.py @@ -0,0 +1,21 @@ +# test - should run true +# test - should run false +# test - invalid parent class but should run true +# test - imported from other file, should not collect +# test - failed to instance + + +from unittest import TestCase +import infection_monkey.utils.plugins.plugins_testcases +from infection_monkey.utils.plugins.load_plugins import get_instances +from infection_monkey.utils.plugins.plugin import Plugin + + +class PluginTester(TestCase): + + def setUp(self): + pass + + def test_plugins(self): + res = get_instances(infection_monkey.utils.plugins.plugins_testcases.__package__,infection_monkey.utils.plugins.plugins_testcases.__file__,Plugin) + self.assertEqual(len(res), 1) diff --git a/monkey/infection_monkey/utils/plugins/plugins_testcases/__init__.py b/monkey/infection_monkey/utils/plugins/plugins_testcases/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_bad_import.py b/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_bad_import.py new file mode 100644 index 000000000..65cbc6e8b --- /dev/null +++ b/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_bad_import.py @@ -0,0 +1 @@ +from infection_monkey.utils.plugins.plugins_testcases.plugin_test_run import PluginShouldRun \ No newline at end of file diff --git a/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_bad_init.py b/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_bad_init.py new file mode 100644 index 000000000..fd21b58bc --- /dev/null +++ b/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_bad_init.py @@ -0,0 +1,11 @@ +from infection_monkey.utils.plugins.plugin import Plugin + + +class PluginShouldRun(Plugin): + + def __init__(self): + raise ValueError("Some Error") + + @staticmethod + def should_run(class_name: str) -> bool: + return True diff --git a/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_bad_parent.py b/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_bad_parent.py new file mode 100644 index 000000000..72a348ea0 --- /dev/null +++ b/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_bad_parent.py @@ -0,0 +1,4 @@ +class PluginNoInherit: + @staticmethod + def should_run(class_name: str) -> bool: + return True diff --git a/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_test_dontrun.py b/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_test_dontrun.py new file mode 100644 index 000000000..88214bbff --- /dev/null +++ b/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_test_dontrun.py @@ -0,0 +1,7 @@ +from infection_monkey.utils.plugins.plugin import Plugin + + +class PluginDontRun(Plugin): + @staticmethod + def should_run(class_name: str) -> bool: + return False diff --git a/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_test_run.py b/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_test_run.py new file mode 100644 index 000000000..ca2f2a523 --- /dev/null +++ b/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_test_run.py @@ -0,0 +1,7 @@ +from infection_monkey.utils.plugins.plugin import Plugin + + +class PluginShouldRun(Plugin): + @staticmethod + def should_run(class_name: str) -> bool: + return True From 69c66072af3b7f233e939eccd3bd2e7f60fadcc3 Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Tue, 12 Nov 2019 19:33:51 +0200 Subject: [PATCH 17/21] Turn get_instances into class method. This leads to package_name and package_file both also being class methods. Now, each plugin family can load itself. Reimplemented Fingerprinters and PBAs to use this interface. No more need for fingerprinter collector --- monkey/infection_monkey/monkey.py | 4 +- monkey/infection_monkey/network/HostFinger.py | 14 ++++- .../network/fingerprinters_collector.py | 17 ------ monkey/infection_monkey/post_breach/pba.py | 10 ++++ .../infection_monkey/utils/plugins/plugin.py | 53 +++++++++++++++++++ 5 files changed, 77 insertions(+), 21 deletions(-) delete mode 100644 monkey/infection_monkey/network/fingerprinters_collector.py diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index 5b472e0a7..3d0b9d3d7 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -6,13 +6,13 @@ import sys import time import infection_monkey.tunnel as tunnel +from infection_monkey.network.HostFinger import HostFinger from infection_monkey.utils.monkey_dir import create_monkey_dir, get_monkey_dir_path, remove_monkey_dir from infection_monkey.utils.monkey_log_path import get_monkey_log_path from infection_monkey.utils.environment import is_windows_os from infection_monkey.config import WormConfiguration from infection_monkey.control import ControlClient from infection_monkey.model import DELAY_DELETE_CMD -from infection_monkey.network.fingerprinters_collector import get_fingerprint_instances from infection_monkey.network.firewall import app as firewall from infection_monkey.network.network_scanner import NetworkScanner from infection_monkey.system_info import SystemInfoCollector @@ -146,7 +146,7 @@ class InfectionMonkey(object): self._exploiters = WormConfiguration.exploiter_classes - self._fingerprint = get_fingerprint_instances() + self._fingerprint = HostFinger.get_instances() if not self._keep_running or not WormConfiguration.alive: break diff --git a/monkey/infection_monkey/network/HostFinger.py b/monkey/infection_monkey/network/HostFinger.py index 22e5b0698..692071160 100644 --- a/monkey/infection_monkey/network/HostFinger.py +++ b/monkey/infection_monkey/network/HostFinger.py @@ -1,10 +1,20 @@ -from abc import ABCMeta, abstractproperty, abstractmethod +from abc import abstractmethod from infection_monkey.config import WormConfiguration from infection_monkey.utils.plugins.plugin import Plugin -class HostFinger(Plugin, metaclass=ABCMeta): +class HostFinger(Plugin): + @staticmethod + def base_package_file(): + import infection_monkey.network # avoid circular imports + return infection_monkey.network.__file__ + + @staticmethod + def base_package_name(): + import infection_monkey.network # avoid circular imports + return infection_monkey.network.__package__ + @property @abstractmethod def _SCANNED_SERVICE(self): diff --git a/monkey/infection_monkey/network/fingerprinters_collector.py b/monkey/infection_monkey/network/fingerprinters_collector.py deleted file mode 100644 index a68e00edf..000000000 --- a/monkey/infection_monkey/network/fingerprinters_collector.py +++ /dev/null @@ -1,17 +0,0 @@ -import logging -from typing import Sequence -from infection_monkey.utils.plugins.load_plugins import get_instances -from infection_monkey.network.HostFinger import HostFinger - -LOG = logging.getLogger(__name__) - - -def get_fingerprint_instances() -> Sequence[HostFinger]: - """ - Returns the fingerprint objects according to configuration as a list - :return: A list of HostFinger objects. - """ - # note this currently assumes we're in the same package as the fingerprinters - # if this changes, this file should be updated - # like when they move into a network plugins folder - return get_instances(__package__, __file__, HostFinger) diff --git a/monkey/infection_monkey/post_breach/pba.py b/monkey/infection_monkey/post_breach/pba.py index 1bb73b604..6b689ec23 100644 --- a/monkey/infection_monkey/post_breach/pba.py +++ b/monkey/infection_monkey/post_breach/pba.py @@ -20,6 +20,16 @@ class PBA(Plugin): Post breach action object. Can be extended to support more than command execution on target machine. """ + @staticmethod + def base_package_name(): + import infection_monkey.post_breach.actions # avoid circular imports + return infection_monkey.post_breach.actions.__package__ + + @staticmethod + def base_package_file(): + import infection_monkey.post_breach.actions + return infection_monkey.post_breach.actions.__file__ + def __init__(self, name="unknown", linux_cmd="", windows_cmd=""): """ :param name: Name of post breach action. diff --git a/monkey/infection_monkey/utils/plugins/plugin.py b/monkey/infection_monkey/utils/plugins/plugin.py index ca122f70d..474afbba8 100644 --- a/monkey/infection_monkey/utils/plugins/plugin.py +++ b/monkey/infection_monkey/utils/plugins/plugin.py @@ -1,4 +1,17 @@ +import importlib +import inspect +import logging from abc import ABCMeta, abstractmethod +from os.path import dirname, basename, isfile, join +import glob +from typing import Sequence + +LOG = logging.getLogger(__name__) + + +def _get_candidate_files(base_package_file): + files = glob.glob(join(dirname(base_package_file), "*.py")) + return [basename(f)[:-3] for f in files if isfile(f) and not f.endswith('__init__.py')] class Plugin(metaclass=ABCMeta): @@ -7,3 +20,43 @@ class Plugin(metaclass=ABCMeta): @abstractmethod def should_run(class_name: str) -> bool: raise NotImplementedError() + + @classmethod + def get_instances(cls) -> Sequence[object]: + """ + Returns the type objects from base_package_spec. + base_package name and file must refer to the same package otherwise bad results + :return: A list of parent_class objects. + """ + objects = [] + candidate_files = _get_candidate_files(cls.base_package_file()) + LOG.info("looking for classes of type {} in {}".format(cls.__name__, cls.base_package_name())) + # Go through all of files + for file in candidate_files: + # Import module from that file + module = importlib.import_module('.' + file, cls.base_package_name()) + # Get all classes in a module + # m[1] because return object is (name,class) + classes = [m[1] for m in inspect.getmembers(module, inspect.isclass) + if ((m[1].__module__ == module.__name__) and issubclass(m[1], cls))] + # Get object from class + for class_object in classes: + LOG.debug("Checking if should run object {}".format(class_object.__name__)) + try: + if class_object.should_run(class_object.__name__): + instance = class_object() + objects.append(instance) + LOG.debug("Added {} to list".format(class_object.__name__)) + except Exception as e: + LOG.warning("Exception {} when checking if {} should run".format(str(e), class_object.__name__)) + return objects + + @staticmethod + @abstractmethod + def base_package_file(): + pass + + @staticmethod + @abstractmethod + def base_package_name(): + pass From 7f58bd9693ef5138c128b745821e6080b47796ab Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Tue, 12 Nov 2019 19:35:57 +0200 Subject: [PATCH 18/21] Remove load_plugin interface. --- .../utils/plugins/load_plugins.py | 45 ------------------- .../utils/plugins/load_plugins_test.py | 21 --------- .../plugins/plugins_testcases/__init__.py | 0 .../plugins_testcases/plugin_bad_import.py | 1 - .../plugins_testcases/plugin_bad_init.py | 11 ----- .../plugins_testcases/plugin_bad_parent.py | 4 -- .../plugins_testcases/plugin_test_dontrun.py | 7 --- .../plugins_testcases/plugin_test_run.py | 7 --- 8 files changed, 96 deletions(-) delete mode 100644 monkey/infection_monkey/utils/plugins/load_plugins.py delete mode 100644 monkey/infection_monkey/utils/plugins/load_plugins_test.py delete mode 100644 monkey/infection_monkey/utils/plugins/plugins_testcases/__init__.py delete mode 100644 monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_bad_import.py delete mode 100644 monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_bad_init.py delete mode 100644 monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_bad_parent.py delete mode 100644 monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_test_dontrun.py delete mode 100644 monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_test_run.py diff --git a/monkey/infection_monkey/utils/plugins/load_plugins.py b/monkey/infection_monkey/utils/plugins/load_plugins.py deleted file mode 100644 index 0f39e70aa..000000000 --- a/monkey/infection_monkey/utils/plugins/load_plugins.py +++ /dev/null @@ -1,45 +0,0 @@ -import importlib -import inspect -import logging -from os.path import dirname, basename, isfile, join -import glob - -from infection_monkey.utils.plugins.plugin import Plugin - -LOG = logging.getLogger(__name__) - - -def _get_candidate_files(base_package_file): - files = glob.glob(join(dirname(base_package_file), "*.py")) - return [basename(f)[:-3] for f in files if isfile(f) and not f.endswith('__init__.py')] - - -def get_instances(base_package_name, base_package_file, parent_class: Plugin): - """ - Returns the parent_class type objects from base_package_spec. - parent_class must be a class object that inherits from Plugin - base_package name and file must refer to the same package otherwise bad results - :return: A list of parent_class objects. - """ - objects = [] - candidate_files = _get_candidate_files(base_package_file) - LOG.info("looking for classes of type {} in {}".format(parent_class.__name__, base_package_name)) - # Go through all of files - for file in candidate_files: - # Import module from that file - module = importlib.import_module('.' + file, base_package_name) - # Get all classes in a module - # m[1] because return object is (name,class) - classes = [m[1] for m in inspect.getmembers(module, inspect.isclass) - if ((m[1].__module__ == module.__name__) and issubclass(m[1], parent_class))] - # Get object from class - for class_object in classes: - LOG.debug("Checking if should run object {}".format(class_object.__name__)) - try: - if class_object.should_run(class_object.__name__): - instance = class_object() - objects.append(instance) - LOG.debug("Added {} to list".format(class_object.__name__)) - except Exception as e: - LOG.warning("Exception {} when checking if {} should run".format(str(e), class_object.__name__)) - return objects diff --git a/monkey/infection_monkey/utils/plugins/load_plugins_test.py b/monkey/infection_monkey/utils/plugins/load_plugins_test.py deleted file mode 100644 index cb77f5d9d..000000000 --- a/monkey/infection_monkey/utils/plugins/load_plugins_test.py +++ /dev/null @@ -1,21 +0,0 @@ -# test - should run true -# test - should run false -# test - invalid parent class but should run true -# test - imported from other file, should not collect -# test - failed to instance - - -from unittest import TestCase -import infection_monkey.utils.plugins.plugins_testcases -from infection_monkey.utils.plugins.load_plugins import get_instances -from infection_monkey.utils.plugins.plugin import Plugin - - -class PluginTester(TestCase): - - def setUp(self): - pass - - def test_plugins(self): - res = get_instances(infection_monkey.utils.plugins.plugins_testcases.__package__,infection_monkey.utils.plugins.plugins_testcases.__file__,Plugin) - self.assertEqual(len(res), 1) diff --git a/monkey/infection_monkey/utils/plugins/plugins_testcases/__init__.py b/monkey/infection_monkey/utils/plugins/plugins_testcases/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_bad_import.py b/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_bad_import.py deleted file mode 100644 index 65cbc6e8b..000000000 --- a/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_bad_import.py +++ /dev/null @@ -1 +0,0 @@ -from infection_monkey.utils.plugins.plugins_testcases.plugin_test_run import PluginShouldRun \ No newline at end of file diff --git a/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_bad_init.py b/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_bad_init.py deleted file mode 100644 index fd21b58bc..000000000 --- a/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_bad_init.py +++ /dev/null @@ -1,11 +0,0 @@ -from infection_monkey.utils.plugins.plugin import Plugin - - -class PluginShouldRun(Plugin): - - def __init__(self): - raise ValueError("Some Error") - - @staticmethod - def should_run(class_name: str) -> bool: - return True diff --git a/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_bad_parent.py b/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_bad_parent.py deleted file mode 100644 index 72a348ea0..000000000 --- a/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_bad_parent.py +++ /dev/null @@ -1,4 +0,0 @@ -class PluginNoInherit: - @staticmethod - def should_run(class_name: str) -> bool: - return True diff --git a/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_test_dontrun.py b/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_test_dontrun.py deleted file mode 100644 index 88214bbff..000000000 --- a/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_test_dontrun.py +++ /dev/null @@ -1,7 +0,0 @@ -from infection_monkey.utils.plugins.plugin import Plugin - - -class PluginDontRun(Plugin): - @staticmethod - def should_run(class_name: str) -> bool: - return False diff --git a/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_test_run.py b/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_test_run.py deleted file mode 100644 index ca2f2a523..000000000 --- a/monkey/infection_monkey/utils/plugins/plugins_testcases/plugin_test_run.py +++ /dev/null @@ -1,7 +0,0 @@ -from infection_monkey.utils.plugins.plugin import Plugin - - -class PluginShouldRun(Plugin): - @staticmethod - def should_run(class_name: str) -> bool: - return True From e9538e9a42f126964e56f3eeeef23a065424cccd Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Tue, 12 Nov 2019 19:39:06 +0200 Subject: [PATCH 19/21] Bugfix, also change PBA interface --- .../post_breach/post_breach_handler.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/monkey/infection_monkey/post_breach/post_breach_handler.py b/monkey/infection_monkey/post_breach/post_breach_handler.py index e3c81cd23..7474c8ef1 100644 --- a/monkey/infection_monkey/post_breach/post_breach_handler.py +++ b/monkey/infection_monkey/post_breach/post_breach_handler.py @@ -1,8 +1,8 @@ import logging +from typing import Sequence + from infection_monkey.utils.environment import is_windows_os -from infection_monkey.utils.plugins.load_plugins import get_instances from infection_monkey.post_breach.pba import PBA -import infection_monkey.post_breach.actions LOG = logging.getLogger(__name__) @@ -33,10 +33,8 @@ class PostBreach(object): LOG.info("All PBAs executed. Total {} executed.".format(len(self.pba_list))) @staticmethod - def config_to_pba_list(): + def config_to_pba_list() -> Sequence[PBA]: """ - Passes config to each post breach action class and aggregates results into a list. :return: A list of PBA objects. """ - return get_instances(infection_monkey.post_breach.actions.__package__, - infection_monkey.post_breach.actions.__file__, PBA) + return PBA.get_instances() From 31a60b12ffa12eb3163fd3f2ac39b08b4620e230 Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Tue, 12 Nov 2019 20:07:48 +0200 Subject: [PATCH 20/21] Fix typing in factory method --- monkey/infection_monkey/utils/plugins/plugin.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/utils/plugins/plugin.py b/monkey/infection_monkey/utils/plugins/plugin.py index 474afbba8..21d3134bf 100644 --- a/monkey/infection_monkey/utils/plugins/plugin.py +++ b/monkey/infection_monkey/utils/plugins/plugin.py @@ -4,7 +4,7 @@ import logging from abc import ABCMeta, abstractmethod from os.path import dirname, basename, isfile, join import glob -from typing import Sequence +from typing import Sequence, TypeVar, Type LOG = logging.getLogger(__name__) @@ -14,6 +14,9 @@ def _get_candidate_files(base_package_file): return [basename(f)[:-3] for f in files if isfile(f) and not f.endswith('__init__.py')] +Plugin_type = TypeVar('Plugin_type', bound='Plugin') + + class Plugin(metaclass=ABCMeta): @staticmethod @@ -22,7 +25,7 @@ class Plugin(metaclass=ABCMeta): raise NotImplementedError() @classmethod - def get_instances(cls) -> Sequence[object]: + def get_instances(cls) -> Sequence[Type[Plugin_type]]: """ Returns the type objects from base_package_spec. base_package name and file must refer to the same package otherwise bad results From 4f59a85f0b6aa87c032d526d94a14737c9cd2b58 Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Wed, 13 Nov 2019 11:10:26 +0200 Subject: [PATCH 21/21] No need to avoid circular imports --- monkey/infection_monkey/network/HostFinger.py | 3 +-- monkey/infection_monkey/post_breach/pba.py | 4 +--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/monkey/infection_monkey/network/HostFinger.py b/monkey/infection_monkey/network/HostFinger.py index 692071160..dbc3b40cd 100644 --- a/monkey/infection_monkey/network/HostFinger.py +++ b/monkey/infection_monkey/network/HostFinger.py @@ -2,17 +2,16 @@ from abc import abstractmethod from infection_monkey.config import WormConfiguration from infection_monkey.utils.plugins.plugin import Plugin +import infection_monkey.network class HostFinger(Plugin): @staticmethod def base_package_file(): - import infection_monkey.network # avoid circular imports return infection_monkey.network.__file__ @staticmethod def base_package_name(): - import infection_monkey.network # avoid circular imports return infection_monkey.network.__package__ @property diff --git a/monkey/infection_monkey/post_breach/pba.py b/monkey/infection_monkey/post_breach/pba.py index 6b689ec23..3d8da9dab 100644 --- a/monkey/infection_monkey/post_breach/pba.py +++ b/monkey/infection_monkey/post_breach/pba.py @@ -7,7 +7,7 @@ from infection_monkey.utils.environment import is_windows_os from infection_monkey.config import WormConfiguration from infection_monkey.telemetry.attack.t1064_telem import T1064Telem from infection_monkey.utils.plugins.plugin import Plugin - +import infection_monkey.post_breach.actions LOG = logging.getLogger(__name__) __author__ = 'VakarisZ' @@ -22,12 +22,10 @@ class PBA(Plugin): @staticmethod def base_package_name(): - import infection_monkey.post_breach.actions # avoid circular imports return infection_monkey.post_breach.actions.__package__ @staticmethod def base_package_file(): - import infection_monkey.post_breach.actions return infection_monkey.post_breach.actions.__file__ def __init__(self, name="unknown", linux_cmd="", windows_cmd=""):