From 43d4c36507ddc5e63175722088c1d26e86aae52d Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Thu, 21 Nov 2019 15:21:41 +0200 Subject: [PATCH] Refactor exploiters into plugins. Change configuration to support it and remove unneeded complex importing. Changed main monkey code to support exploiter plugins. --- monkey/infection_monkey/config.py | 15 +-- .../infection_monkey/exploit/HostExploiter.py | 98 +++++++++++++++++++ monkey/infection_monkey/exploit/__init__.py | 92 ----------------- monkey/infection_monkey/exploit/mssqlexec.py | 2 +- monkey/infection_monkey/exploit/sambacry.py | 2 +- monkey/infection_monkey/exploit/shellshock.py | 2 +- monkey/infection_monkey/exploit/smbexec.py | 2 +- monkey/infection_monkey/exploit/sshexec.py | 2 +- monkey/infection_monkey/exploit/vsftpd.py | 2 +- monkey/infection_monkey/exploit/web_rce.py | 2 +- monkey/infection_monkey/exploit/weblogic.py | 2 +- .../infection_monkey/exploit/win_ms08_067.py | 2 +- monkey/infection_monkey/exploit/wmiexec.py | 2 +- monkey/infection_monkey/monkey.py | 8 +- 14 files changed, 116 insertions(+), 117 deletions(-) create mode 100644 monkey/infection_monkey/exploit/HostExploiter.py diff --git a/monkey/infection_monkey/config.py b/monkey/infection_monkey/config.py index 5e52022cd..19682f2b4 100644 --- a/monkey/infection_monkey/config.py +++ b/monkey/infection_monkey/config.py @@ -20,10 +20,6 @@ HIDDEN_FIELD_REPLACEMENT_CONTENT = "hidden" class Configuration(object): def from_kv(self, formatted_data): - # now we won't work at <2.7 for sure - network_import = importlib.import_module('infection_monkey.network') - exploit_import = importlib.import_module('infection_monkey.exploit') - unknown_items = [] for key, value in list(formatted_data.items()): if key.startswith('_'): @@ -32,15 +28,10 @@ class Configuration(object): continue if self._depth_from_commandline and key == "depth": continue - # handle in cases - elif key == 'exploiter_classes': - class_objects = [getattr(exploit_import, val) for val in value] - setattr(self, key, class_objects) + if hasattr(self, key): + setattr(self, key, value) else: - if hasattr(self, key): - setattr(self, key, value) - else: - unknown_items.append(key) + unknown_items.append(key) return unknown_items def from_json(self, json_data): diff --git a/monkey/infection_monkey/exploit/HostExploiter.py b/monkey/infection_monkey/exploit/HostExploiter.py new file mode 100644 index 000000000..50f4167d8 --- /dev/null +++ b/monkey/infection_monkey/exploit/HostExploiter.py @@ -0,0 +1,98 @@ +from abc import abstractmethod + +from infection_monkey.config import WormConfiguration +from common.utils.exploit_enum import ExploitType +from datetime import datetime + +from infection_monkey.utils.plugins.plugin import Plugin +import infection_monkey.exploit + +__author__ = 'itamar' + + +class HostExploiter(Plugin): + @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.exploiter_classes + + @staticmethod + def base_package_file(): + return infection_monkey.exploit.__file__ + + @staticmethod + def base_package_name(): + return infection_monkey.exploit.__package__ + + _TARGET_OS_TYPE = [] + + # Usual values are 'vulnerability' or 'brute_force' + EXPLOIT_TYPE = ExploitType.VULNERABILITY + + @property + @abstractmethod + def _EXPLOITED_SERVICE(self): + pass + + def __init__(self, host): + self._config = WormConfiguration + self.exploit_info = {'display_name': self._EXPLOITED_SERVICE, + 'started': '', + 'finished': '', + 'vulnerable_urls': [], + 'vulnerable_ports': [], + 'executed_cmds': []} + self.exploit_attempts = [] + self.host = host + + def set_start_time(self): + self.exploit_info['started'] = datetime.now().isoformat() + + def set_finish_time(self): + self.exploit_info['finished'] = datetime.now().isoformat() + + def is_os_supported(self): + return self.host.os.get('type') in self._TARGET_OS_TYPE + + def send_exploit_telemetry(self, result): + from infection_monkey.telemetry.exploit_telem import ExploitTelem + ExploitTelem(self, result).send() + + def report_login_attempt(self, result, user, password='', lm_hash='', ntlm_hash='', ssh_key=''): + self.exploit_attempts.append({'result': result, 'user': user, 'password': password, + 'lm_hash': lm_hash, 'ntlm_hash': ntlm_hash, 'ssh_key': ssh_key}) + + def exploit_host(self): + self.pre_exploit() + try: + result = self._exploit_host() + finally: + self.post_exploit() + return result + + def pre_exploit(self): + self.set_start_time() + + def post_exploit(self): + self.set_finish_time() + + @abstractmethod + def _exploit_host(self): + raise NotImplementedError() + + def add_vuln_url(self, url): + self.exploit_info['vulnerable_urls'].append(url) + + def add_vuln_port(self, port): + self.exploit_info['vulnerable_ports'].append(port) + + def add_executed_cmd(self, cmd): + """ + Appends command to exploiter's info. + :param cmd: String of executed command. e.g. 'echo Example' + """ + powershell = True if "powershell" in cmd.lower() else False + self.exploit_info['executed_cmds'].append({'cmd': cmd, 'powershell': powershell}) diff --git a/monkey/infection_monkey/exploit/__init__.py b/monkey/infection_monkey/exploit/__init__.py index 306350437..e69de29bb 100644 --- a/monkey/infection_monkey/exploit/__init__.py +++ b/monkey/infection_monkey/exploit/__init__.py @@ -1,92 +0,0 @@ -from abc import ABCMeta, abstractmethod, abstractproperty -import infection_monkey.config -from common.utils.exploit_enum import ExploitType -from datetime import datetime - -__author__ = 'itamar' - - -class HostExploiter(object, metaclass=ABCMeta): - _TARGET_OS_TYPE = [] - - # Usual values are 'vulnerability' or 'brute_force' - EXPLOIT_TYPE = ExploitType.VULNERABILITY - - @property - @abstractmethod - def _EXPLOITED_SERVICE(self): - pass - - def __init__(self, host): - self._config = infection_monkey.config.WormConfiguration - self.exploit_info = {'display_name': self._EXPLOITED_SERVICE, - 'started': '', - 'finished': '', - 'vulnerable_urls': [], - 'vulnerable_ports': [], - 'executed_cmds': []} - self.exploit_attempts = [] - self.host = host - - def set_start_time(self): - self.exploit_info['started'] = datetime.now().isoformat() - - def set_finish_time(self): - self.exploit_info['finished'] = datetime.now().isoformat() - - def is_os_supported(self): - return self.host.os.get('type') in self._TARGET_OS_TYPE - - def send_exploit_telemetry(self, result): - from infection_monkey.telemetry.exploit_telem import ExploitTelem - ExploitTelem(self, result).send() - - def report_login_attempt(self, result, user, password='', lm_hash='', ntlm_hash='', ssh_key=''): - self.exploit_attempts.append({'result': result, 'user': user, 'password': password, - 'lm_hash': lm_hash, 'ntlm_hash': ntlm_hash, 'ssh_key': ssh_key}) - - def exploit_host(self): - self.pre_exploit() - try: - result = self._exploit_host() - finally: - self.post_exploit() - return result - - def pre_exploit(self): - self.set_start_time() - - def post_exploit(self): - self.set_finish_time() - - @abstractmethod - def _exploit_host(self): - raise NotImplementedError() - - def add_vuln_url(self, url): - self.exploit_info['vulnerable_urls'].append(url) - - def add_vuln_port(self, port): - self.exploit_info['vulnerable_ports'].append(port) - - def add_executed_cmd(self, cmd): - """ - Appends command to exploiter's info. - :param cmd: String of executed command. e.g. 'echo Example' - """ - powershell = True if "powershell" in cmd.lower() else False - self.exploit_info['executed_cmds'].append({'cmd': cmd, 'powershell': powershell}) - - -from infection_monkey.exploit.win_ms08_067 import Ms08_067_Exploiter -from infection_monkey.exploit.wmiexec import WmiExploiter -from infection_monkey.exploit.smbexec import SmbExploiter -from infection_monkey.exploit.sshexec import SSHExploiter -from infection_monkey.exploit.shellshock import ShellShockExploiter -from infection_monkey.exploit.sambacry import SambaCryExploiter -from infection_monkey.exploit.elasticgroovy import ElasticGroovyExploiter -from infection_monkey.exploit.struts2 import Struts2Exploiter -from infection_monkey.exploit.weblogic import WebLogicExploiter -from infection_monkey.exploit.hadoop import HadoopExploiter -from infection_monkey.exploit.mssqlexec import MSSQLExploiter -from infection_monkey.exploit.vsftpd import VSFTPDExploiter diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index 16e69dff0..9d2aff5b0 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -6,7 +6,7 @@ from time import sleep import pymssql from common.utils.exploit_enum import ExploitType -from infection_monkey.exploit import HostExploiter +from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.tools.http_tools import MonkeyHTTPServer from infection_monkey.exploit.tools.helpers import get_monkey_dest_path, build_monkey_commandline, get_monkey_depth from infection_monkey.model import DROPPER_ARG diff --git a/monkey/infection_monkey/exploit/sambacry.py b/monkey/infection_monkey/exploit/sambacry.py index 4e1d71fd9..4820d0f05 100644 --- a/monkey/infection_monkey/exploit/sambacry.py +++ b/monkey/infection_monkey/exploit/sambacry.py @@ -16,7 +16,7 @@ from impacket.smb3structs import SMB2_IL_IMPERSONATION, SMB2_CREATE, SMB2_FLAGS_ from impacket.smbconnection import SMBConnection import infection_monkey.monkeyfs as monkeyfs -from infection_monkey.exploit import HostExploiter +from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.model import DROPPER_ARG from infection_monkey.network.smbfinger import SMB_SERVICE from infection_monkey.exploit.tools.helpers import build_monkey_commandline, get_target_monkey_by_os, get_monkey_depth diff --git a/monkey/infection_monkey/exploit/shellshock.py b/monkey/infection_monkey/exploit/shellshock.py index f65974dbd..203d39b9b 100644 --- a/monkey/infection_monkey/exploit/shellshock.py +++ b/monkey/infection_monkey/exploit/shellshock.py @@ -7,7 +7,7 @@ from random import choice import requests from common.utils.attack_utils import ScanStatus -from infection_monkey.exploit import HostExploiter +from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.tools.helpers import get_target_monkey, get_monkey_depth, build_monkey_commandline from infection_monkey.model import DROPPER_ARG from infection_monkey.exploit.shellshock_resources import CGI_FILES diff --git a/monkey/infection_monkey/exploit/smbexec.py b/monkey/infection_monkey/exploit/smbexec.py index fef8dad05..f53e1ac38 100644 --- a/monkey/infection_monkey/exploit/smbexec.py +++ b/monkey/infection_monkey/exploit/smbexec.py @@ -3,7 +3,7 @@ from logging import getLogger from impacket.dcerpc.v5 import transport, scmr from impacket.smbconnection import SMB_DIALECT -from infection_monkey.exploit import HostExploiter +from infection_monkey.exploit.HostExploiter 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 diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index a7e9571b5..4fbc484eb 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -5,7 +5,7 @@ import time import paramiko import infection_monkey.monkeyfs as monkeyfs -from infection_monkey.exploit import HostExploiter +from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.tools.helpers import get_target_monkey, get_monkey_depth, build_monkey_commandline from infection_monkey.model import MONKEY_ARG from infection_monkey.network.tools import check_tcp_port, get_interface_to_target diff --git a/monkey/infection_monkey/exploit/vsftpd.py b/monkey/infection_monkey/exploit/vsftpd.py index d4116c96c..82954b99b 100644 --- a/monkey/infection_monkey/exploit/vsftpd.py +++ b/monkey/infection_monkey/exploit/vsftpd.py @@ -8,7 +8,7 @@ import socket import time from common.utils.attack_utils import ScanStatus -from infection_monkey.exploit import HostExploiter +from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.tools.helpers import get_target_monkey, build_monkey_commandline, get_monkey_depth from infection_monkey.exploit.tools.http_tools import HTTPTools from infection_monkey.model import MONKEY_ARG, CHMOD_MONKEY, RUN_MONKEY, WGET_HTTP_UPLOAD, DOWNLOAD_TIMEOUT diff --git a/monkey/infection_monkey/exploit/web_rce.py b/monkey/infection_monkey/exploit/web_rce.py index b894acf43..bef428f4d 100644 --- a/monkey/infection_monkey/exploit/web_rce.py +++ b/monkey/infection_monkey/exploit/web_rce.py @@ -3,7 +3,7 @@ import re from posixpath import join from abc import abstractmethod -from infection_monkey.exploit import HostExploiter +from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.tools.helpers import get_target_monkey, get_monkey_depth, build_monkey_commandline from infection_monkey.exploit.tools.http_tools import HTTPTools from infection_monkey.model import CHECK_COMMAND, ID_STRING, GET_ARCH_LINUX, GET_ARCH_WINDOWS, BITSADMIN_CMDLINE_HTTP, \ diff --git a/monkey/infection_monkey/exploit/weblogic.py b/monkey/infection_monkey/exploit/weblogic.py index 08b642942..7ea80b372 100644 --- a/monkey/infection_monkey/exploit/weblogic.py +++ b/monkey/infection_monkey/exploit/weblogic.py @@ -7,7 +7,7 @@ from requests import post, exceptions from http.server import BaseHTTPRequestHandler, HTTPServer from infection_monkey.exploit.web_rce import WebRCE -from infection_monkey.exploit import HostExploiter +from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.network.tools import get_interface_to_target from infection_monkey.network.info import get_free_tcp_port from http.server import BaseHTTPRequestHandler, HTTPServer diff --git a/monkey/infection_monkey/exploit/win_ms08_067.py b/monkey/infection_monkey/exploit/win_ms08_067.py index b84cf3391..f296091d6 100644 --- a/monkey/infection_monkey/exploit/win_ms08_067.py +++ b/monkey/infection_monkey/exploit/win_ms08_067.py @@ -19,7 +19,7 @@ from infection_monkey.exploit.tools.smb_tools import SmbTools from infection_monkey.model import DROPPER_CMDLINE_WINDOWS, MONKEY_CMDLINE_WINDOWS from infection_monkey.network.smbfinger import SMBFinger from infection_monkey.network.tools import check_tcp_port -from . import HostExploiter +from infection_monkey.exploit.HostExploiter import HostExploiter LOG = getLogger(__name__) diff --git a/monkey/infection_monkey/exploit/wmiexec.py b/monkey/infection_monkey/exploit/wmiexec.py index cc286bfcd..adaf524e2 100644 --- a/monkey/infection_monkey/exploit/wmiexec.py +++ b/monkey/infection_monkey/exploit/wmiexec.py @@ -5,7 +5,7 @@ import traceback from impacket.dcerpc.v5.rpcrt import DCERPCException -from infection_monkey.exploit import HostExploiter +from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.tools.helpers import get_target_monkey, \ get_monkey_depth, build_monkey_commandline from infection_monkey.exploit.tools.wmi_tools import AccessDeniedException diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index 3d0b9d3d7..d9af43cae 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -30,6 +30,7 @@ from infection_monkey.network.tools import get_interface_to_target from infection_monkey.exploit.tools.exceptions import ExploitingVulnerableMachineError, FailedExploitationError from infection_monkey.telemetry.attack.t1106_telem import T1106Telem from common.utils.attack_utils import ScanStatus, UsageEnum +from infection_monkey.exploit.HostExploiter import HostExploiter __author__ = 'itamar' @@ -144,10 +145,10 @@ class InfectionMonkey(object): self._network.initialize() - self._exploiters = WormConfiguration.exploiter_classes - self._fingerprint = HostFinger.get_instances() + self._exploiters = HostExploiter.get_classes() + if not self._keep_running or not WormConfiguration.alive: break @@ -183,7 +184,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))