diff --git a/monkey/common/utils/exploit_enum.py b/monkey/common/utils/exploit_enum.py new file mode 100644 index 000000000..3aff53121 --- /dev/null +++ b/monkey/common/utils/exploit_enum.py @@ -0,0 +1,7 @@ +from enum import Enum + + +class ExploitType(Enum): + VULNERABILITY = 1 + OTHER = 8 + BRUTE_FORCE = 9 diff --git a/monkey/infection_monkey/exploit/__init__.py b/monkey/infection_monkey/exploit/__init__.py index 9ea2bcc75..0d4300b5f 100644 --- a/monkey/infection_monkey/exploit/__init__.py +++ b/monkey/infection_monkey/exploit/__init__.py @@ -1,5 +1,6 @@ from abc import ABCMeta, abstractmethod import infection_monkey.config +from common.utils.exploit_enum import ExploitType __author__ = 'itamar' @@ -9,6 +10,9 @@ class HostExploiter(object): _TARGET_OS_TYPE = [] + # Usual values are 'vulnerability' or 'brute_force' + EXPLOIT_TYPE = ExploitType.VULNERABILITY + def __init__(self, host): self._config = infection_monkey.config.WormConfiguration self._exploit_info = {} diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index 128755de0..43ef43b7e 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -6,6 +6,7 @@ import logging import pymssql from infection_monkey.exploit import HostExploiter, mssqlexec_utils +from common.utils.exploit_enum import ExploitType __author__ = 'Maor Rayzin' @@ -15,6 +16,7 @@ LOG = logging.getLogger(__name__) class MSSQLExploiter(HostExploiter): _TARGET_OS_TYPE = ['windows'] + EXPLOIT_TYPE = ExploitType.BRUTE_FORCE LOGIN_TIMEOUT = 15 SQL_DEFAULT_TCP_PORT = '1433' DEFAULT_PAYLOAD_PATH = os.path.expandvars(r'%TEMP%\~PLD123.bat') if platform.system() else '/tmp/~PLD123.bat' diff --git a/monkey/infection_monkey/exploit/rdpgrinder.py b/monkey/infection_monkey/exploit/rdpgrinder.py index a67a812f6..dcef9551c 100644 --- a/monkey/infection_monkey/exploit/rdpgrinder.py +++ b/monkey/infection_monkey/exploit/rdpgrinder.py @@ -16,6 +16,7 @@ from infection_monkey.model import RDP_CMDLINE_HTTP_BITS, RDP_CMDLINE_HTTP_VBS from infection_monkey.network.tools import check_tcp_port from infection_monkey.exploit.tools import build_monkey_commandline from infection_monkey.utils import utf_to_ascii +from common.utils.exploit_enum import ExploitType __author__ = 'hoffer' @@ -235,6 +236,7 @@ class CMDClientFactory(rdp.ClientFactory): class RdpExploiter(HostExploiter): _TARGET_OS_TYPE = ['windows'] + EXPLOIT_TYPE = ExploitType.BRUTE_FORCE def __init__(self, host): super(RdpExploiter, self).__init__(host) diff --git a/monkey/infection_monkey/exploit/smbexec.py b/monkey/infection_monkey/exploit/smbexec.py index 7528e08ba..579fd8f1f 100644 --- a/monkey/infection_monkey/exploit/smbexec.py +++ b/monkey/infection_monkey/exploit/smbexec.py @@ -9,12 +9,14 @@ from infection_monkey.model import MONKEY_CMDLINE_DETACHED_WINDOWS, DROPPER_CMDL from infection_monkey.network import SMBFinger from infection_monkey.network.tools import check_tcp_port from infection_monkey.exploit.tools import build_monkey_commandline +from common.utils.exploit_enum import ExploitType LOG = getLogger(__name__) class SmbExploiter(HostExploiter): _TARGET_OS_TYPE = ['windows'] + EXPLOIT_TYPE = ExploitType.BRUTE_FORCE KNOWN_PROTOCOLS = { '139/SMB': (r'ncacn_np:%s[\pipe\svcctl]', 139), '445/SMB': (r'ncacn_np:%s[\pipe\svcctl]', 445), diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 82dd1f4d7..8a58f18c6 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -10,6 +10,7 @@ from infection_monkey.exploit.tools import get_target_monkey, get_monkey_depth from infection_monkey.model import MONKEY_ARG from infection_monkey.network.tools import check_tcp_port from infection_monkey.exploit.tools import build_monkey_commandline +from common.utils.exploit_enum import ExploitType __author__ = 'hoffer' @@ -20,6 +21,7 @@ TRANSFER_UPDATE_RATE = 15 class SSHExploiter(HostExploiter): _TARGET_OS_TYPE = ['linux', None] + EXPLOIT_TYPE = ExploitType.BRUTE_FORCE def __init__(self, host): super(SSHExploiter, self).__init__(host) diff --git a/monkey/infection_monkey/exploit/wmiexec.py b/monkey/infection_monkey/exploit/wmiexec.py index 1a8cb3386..66cc30fa9 100644 --- a/monkey/infection_monkey/exploit/wmiexec.py +++ b/monkey/infection_monkey/exploit/wmiexec.py @@ -9,12 +9,14 @@ from infection_monkey.exploit import HostExploiter from infection_monkey.exploit.tools import SmbTools, WmiTools, AccessDeniedException, get_target_monkey, \ get_monkey_depth, build_monkey_commandline from infection_monkey.model import DROPPER_CMDLINE_WINDOWS, MONKEY_CMDLINE_WINDOWS +from common.utils.exploit_enum import ExploitType LOG = logging.getLogger(__name__) class WmiExploiter(HostExploiter): _TARGET_OS_TYPE = ['windows'] + EXPLOIT_TYPE = ExploitType.BRUTE_FORCE def __init__(self, host): super(WmiExploiter, self).__init__(host) diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index f2f9f4f42..92913749e 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -167,44 +167,17 @@ class InfectionMonkey(object): LOG.debug("Default server: %s set to machine: %r" % (self._default_server, machine)) machine.set_default_server(self._default_server) - successful_exploiter = None + # Order exploits according to their type + self._exploiters = sorted(self._exploiters, key=lambda exploiter_: exploiter_.EXPLOIT_TYPE.value) + host_exploited = False for exploiter in [exploiter(machine) for exploiter in self._exploiters]: - if not exploiter.is_os_supported(): - LOG.info("Skipping exploiter %s host:%r, os is not supported", - exploiter.__class__.__name__, machine) - continue - - LOG.info("Trying to exploit %r with exploiter %s...", machine, exploiter.__class__.__name__) - - result = False - try: - result = exploiter.exploit_host() - if result: - successful_exploiter = exploiter - break - else: - LOG.info("Failed exploiting %r with exploiter %s", machine, exploiter.__class__.__name__) - - except Exception as exc: - LOG.exception("Exception while attacking %s using %s: %s", - machine, exploiter.__class__.__name__, exc) - finally: - exploiter.send_exploit_telemetry(result) - - if successful_exploiter: - self._exploited_machines.add(machine) - - LOG.info("Successfully propagated to %s using %s", - machine, successful_exploiter.__class__.__name__) - - # check if max-exploitation limit is reached - if WormConfiguration.victims_max_exploit <= len(self._exploited_machines): - self._keep_running = False - - LOG.info("Max exploited victims reached (%d)", WormConfiguration.victims_max_exploit) + if self.try_exploiting(machine, exploiter): + host_exploited = True break - else: + if not host_exploited: self._fail_exploitation_machines.add(machine) + if not self._keep_running: + break if (not is_empty) and (WormConfiguration.max_iterations > iteration_index + 1): time_to_sleep = WormConfiguration.timeout_between_iterations @@ -279,3 +252,50 @@ class InfectionMonkey(object): log = '' ControlClient.send_log(log) + + def try_exploiting(self, machine, exploiter): + """ + Workflow of exploiting one machine with one exploiter + :param machine: Machine monkey tries to exploit + :param exploiter: Exploiter to use on that machine + :return: True if successfully exploited, False otherwise + """ + if not exploiter.is_os_supported(): + LOG.info("Skipping exploiter %s host:%r, os is not supported", + exploiter.__class__.__name__, machine) + return False + + LOG.info("Trying to exploit %r with exploiter %s...", machine, exploiter.__class__.__name__) + + result = False + try: + result = exploiter.exploit_host() + if result: + self.successfully_exploited(machine, exploiter) + return True + else: + LOG.info("Failed exploiting %r with exploiter %s", machine, exploiter.__class__.__name__) + + except Exception as exc: + LOG.exception("Exception while attacking %s using %s: %s", + machine, exploiter.__class__.__name__, exc) + finally: + exploiter.send_exploit_telemetry(result) + return False + + def successfully_exploited(self, machine, exploiter): + """ + Workflow of registering successfully exploited machine + :param machine: machine that was exploited + :param exploiter: exploiter that succeeded + """ + self._exploited_machines.add(machine) + + LOG.info("Successfully propagated to %s using %s", + machine, exploiter.__class__.__name__) + + # check if max-exploitation limit is reached + if WormConfiguration.victims_max_exploit <= len(self._exploited_machines): + self._keep_running = False + + LOG.info("Max exploited victims reached (%d)", WormConfiguration.victims_max_exploit)