Agent: Decouple dropper target paths from the config

* Dropper target paths are now part of infection_monkey.model
This commit is contained in:
Ilija Lazoroski 2022-05-31 13:59:08 +02:00
parent 49654d4dfe
commit b8e943f7a8
14 changed files with 48 additions and 42 deletions

View File

@ -43,7 +43,7 @@ class HadoopExploiter(WebRCE):
return self.exploit_result return self.exploit_result
try: try:
monkey_path_on_victim = get_agent_dest_path(self.host, self.options) monkey_path_on_victim = get_agent_dest_path(self.host)
except KeyError: except KeyError:
return self.exploit_result return self.exploit_result

View File

@ -61,7 +61,7 @@ class Log4ShellExploiter(WebRCE):
self._agent_http_server_thread = None self._agent_http_server_thread = None
def _start_servers(self): def _start_servers(self):
target_path = get_agent_dest_path(self.host, self.options) target_path = get_agent_dest_path(self.host)
# Start http server, to serve agent to victims # Start http server, to serve agent to victims
agent_http_path = self._start_agent_http_server(target_path) agent_http_path = self._start_agent_http_server(target_path)

View File

@ -59,7 +59,7 @@ class MSSQLExploiter(HostExploiter):
Also, don't forget to start_monkey_server() before self.upload_monkey() and Also, don't forget to start_monkey_server() before self.upload_monkey() and
self.stop_monkey_server() after self.stop_monkey_server() after
""" """
monkey_path_on_victim = get_agent_dest_path(self.host, self.options) monkey_path_on_victim = get_agent_dest_path(self.host)
# Brute force to get connection # Brute force to get connection
creds = generate_identity_secret_pairs( creds = generate_identity_secret_pairs(

View File

@ -134,7 +134,7 @@ class PowerShellExploiter(HostExploiter):
raise ValueError(f"Unknown secret type {credentials.secret_type}") raise ValueError(f"Unknown secret type {credentials.secret_type}")
def _execute_monkey_agent_on_victim(self): def _execute_monkey_agent_on_victim(self):
monkey_path_on_victim = get_agent_dest_path(self.host, self.options) monkey_path_on_victim = get_agent_dest_path(self.host)
self._copy_monkey_binary_to_victim(monkey_path_on_victim) self._copy_monkey_binary_to_victim(monkey_path_on_victim)
logger.info("Successfully copied the monkey binary to the victim.") logger.info("Successfully copied the monkey binary to the victim.")

View File

@ -31,7 +31,7 @@ class SMBExploiter(HostExploiter):
def _exploit_host(self): def _exploit_host(self):
agent_binary = self.agent_repository.get_agent_binary(self.host.os["type"]) agent_binary = self.agent_repository.get_agent_binary(self.host.os["type"])
dest_path = get_agent_dest_path(self.host, self.options) dest_path = get_agent_dest_path(self.host)
creds = generate_brute_force_combinations(self.options["credentials"]) creds = generate_brute_force_combinations(self.options["credentials"])
for user, password, lm_hash, ntlm_hash in interruptible_iter(creds, self.interrupt): for user, password, lm_hash, ntlm_hash in interruptible_iter(creds, self.interrupt):

View File

@ -209,7 +209,7 @@ class SSHExploiter(HostExploiter):
self._set_interrupted() self._set_interrupted()
return self.exploit_result return self.exploit_result
monkey_path_on_victim = get_agent_dest_path(self.host, self.options) monkey_path_on_victim = get_agent_dest_path(self.host)
try: try:
with ssh.open_sftp() as ftp: with ssh.open_sftp() as ftp:

View File

@ -2,9 +2,8 @@ import logging
import random import random
import string import string
from pathlib import PurePath, PurePosixPath, PureWindowsPath from pathlib import PurePath, PurePosixPath, PureWindowsPath
from typing import Any, Mapping
from infection_monkey.model import VictimHost from infection_monkey.model import DROPPER_TARGET_PATH_LINUX, DROPPER_TARGET_PATH_WIN64, VictimHost
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -18,11 +17,11 @@ def get_random_file_suffix() -> str:
return random_string return random_string
def get_agent_dest_path(host: VictimHost, options: Mapping[str, Any]) -> PurePath: def get_agent_dest_path(host: VictimHost) -> PurePath:
if host.os["type"] == "windows": if host.os["type"] == "windows":
path = PureWindowsPath(options["dropper_target_path_win_64"]) path = PureWindowsPath(DROPPER_TARGET_PATH_WIN64)
else: else:
path = PurePosixPath(options["dropper_target_path_linux"]) path = PurePosixPath(DROPPER_TARGET_PATH_LINUX)
return _add_random_suffix(path) return _add_random_suffix(path)

View File

@ -12,6 +12,8 @@ from infection_monkey.model import (
CHMOD_MONKEY, CHMOD_MONKEY,
DOWNLOAD_TIMEOUT, DOWNLOAD_TIMEOUT,
DROPPER_ARG, DROPPER_ARG,
DROPPER_TARGET_PATH_LINUX,
DROPPER_TARGET_PATH_WIN64,
ID_STRING, ID_STRING,
MONKEY_ARG, MONKEY_ARG,
POWERSHELL_HTTP_UPLOAD, POWERSHELL_HTTP_UPLOAD,
@ -109,8 +111,8 @@ class WebRCE(HostExploiter):
def pre_exploit(self): def pre_exploit(self):
if not self.monkey_target_paths: if not self.monkey_target_paths:
self.monkey_target_paths = { self.monkey_target_paths = {
"linux": self.options["dropper_target_path_linux"], "linux": DROPPER_TARGET_PATH_LINUX,
"windows": self.options["dropper_target_path_win_64"], "windows": DROPPER_TARGET_PATH_WIN64,
} }
self.HTTP = [str(port) for port in self.options["http_ports"]] self.HTTP = [str(port) for port in self.options["http_ports"]]
super().pre_exploit() super().pre_exploit()
@ -405,7 +407,6 @@ class WebRCE(HostExploiter):
""" """
Gets default dropper path for the host. Gets default dropper path for the host.
:return: Default monkey's destination path for corresponding host or False if failed. :return: Default monkey's destination path for corresponding host or False if failed.
E.g. config.dropper_target_path_linux(/tmp/monkey.sh) for linux host
""" """
if not self.host.os.get("type") or ( if not self.host.os.get("type") or (
self.host.os["type"] != "linux" and self.host.os["type"] != "windows" self.host.os["type"] != "linux" and self.host.os["type"] != "windows"
@ -413,9 +414,9 @@ class WebRCE(HostExploiter):
logger.error("Target's OS was either unidentified or not supported. Aborting") logger.error("Target's OS was either unidentified or not supported. Aborting")
return False return False
if self.host.os["type"] == "linux": if self.host.os["type"] == "linux":
return self.options["dropper_target_path_linux"] return DROPPER_TARGET_PATH_LINUX
if self.host.os["type"] == "windows": if self.host.os["type"] == "windows":
return self.options["dropper_target_path_win_64"] return DROPPER_TARGET_PATH_WIN64
def get_target_url(self): def get_target_url(self):
""" """

View File

@ -10,7 +10,11 @@ from infection_monkey.exploit.tools.helpers import get_agent_dest_path
from infection_monkey.exploit.tools.smb_tools import SmbTools from infection_monkey.exploit.tools.smb_tools import SmbTools
from infection_monkey.exploit.tools.wmi_tools import AccessDeniedException, WmiTools from infection_monkey.exploit.tools.wmi_tools import AccessDeniedException, WmiTools
from infection_monkey.i_puppet import ExploiterResultData from infection_monkey.i_puppet import ExploiterResultData
from infection_monkey.model import DROPPER_CMDLINE_WINDOWS, MONKEY_CMDLINE_WINDOWS from infection_monkey.model import (
DROPPER_CMDLINE_WINDOWS,
DROPPER_TARGET_PATH_WIN64,
MONKEY_CMDLINE_WINDOWS,
)
from infection_monkey.utils.brute_force import ( from infection_monkey.utils.brute_force import (
generate_brute_force_combinations, generate_brute_force_combinations,
get_credential_string, get_credential_string,
@ -74,7 +78,7 @@ class WmiExploiter(HostExploiter):
self._set_interrupted() self._set_interrupted()
return self.exploit_result return self.exploit_result
target_path = get_agent_dest_path(self.host, self.options) target_path = get_agent_dest_path(self.host)
remote_full_path = SmbTools.copy_file( remote_full_path = SmbTools.copy_file(
self.host, self.host,
@ -91,13 +95,13 @@ class WmiExploiter(HostExploiter):
wmi_connection.close() wmi_connection.close()
return self.exploit_result return self.exploit_result
# execute the remote dropper in case the path isn't final # execute the remote dropper in case the path isn't final
elif remote_full_path.lower() != self.options["dropper_target_path_win_64"]: elif remote_full_path.lower() != DROPPER_TARGET_PATH_WIN64:
cmdline = DROPPER_CMDLINE_WINDOWS % { cmdline = DROPPER_CMDLINE_WINDOWS % {
"dropper_path": remote_full_path "dropper_path": remote_full_path
} + build_monkey_commandline( } + build_monkey_commandline(
self.host, self.host,
self.current_depth - 1, self.current_depth - 1,
self.options["dropper_target_path_win_64"], DROPPER_TARGET_PATH_WIN64,
) )
else: else:
cmdline = MONKEY_CMDLINE_WINDOWS % { cmdline = MONKEY_CMDLINE_WINDOWS % {

View File

@ -5,6 +5,10 @@ MONKEY_ARG = "m0nk3y"
DROPPER_ARG = "dr0pp3r" DROPPER_ARG = "dr0pp3r"
ID_STRING = "M0NK3Y3XPL0ITABLE" ID_STRING = "M0NK3Y3XPL0ITABLE"
# Dropper target paths
DROPPER_TARGET_PATH_LINUX = "/tmp/monkey"
DROPPER_TARGET_PATH_WIN64 = r"C:\Windows\temp\monkey64.exe"
# Username prefix for users created by Infection Monkey # Username prefix for users created by Infection Monkey
USERNAME_PREFIX = "somenewuser" USERNAME_PREFIX = "somenewuser"

View File

@ -21,8 +21,6 @@
"custom_PBA_linux_cmd": "bash test.sh", "custom_PBA_linux_cmd": "bash test.sh",
"custom_PBA_windows_cmd": "powershell test.ps1", "custom_PBA_windows_cmd": "powershell test.ps1",
"depth": 2, "depth": 2,
"dropper_target_path_linux": "/tmp/monkey",
"dropper_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe",
"exploit_lm_hash_list": ["lm_hash_1", "lm_hash_2"], "exploit_lm_hash_list": ["lm_hash_1", "lm_hash_2"],
"exploit_ntlm_hash_list": ["nt_hash_1", "nt_hash_2", "nt_hash_3"], "exploit_ntlm_hash_list": ["nt_hash_1", "nt_hash_2", "nt_hash_3"],
"exploit_password_list": [ "exploit_password_list": [

View File

@ -96,10 +96,6 @@
"ElasticFinger" "ElasticFinger"
] ]
}, },
"dropper": {
"dropper_target_path_linux": "/tmp/monkey",
"dropper_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe"
},
"exploits": { "exploits": {
"exploit_lm_hash_list": [], "exploit_lm_hash_list": [],
"exploit_ntlm_hash_list": [], "exploit_ntlm_hash_list": [],

View File

@ -5,6 +5,8 @@ from unittest.mock import MagicMock
import pytest import pytest
from infection_monkey.exploit import powershell from infection_monkey.exploit import powershell
from infection_monkey.exploit.tools.helpers import RAND_SUFFIX_LEN
from infection_monkey.model import DROPPER_TARGET_PATH_WIN64
from infection_monkey.model.host import VictimHost from infection_monkey.model.host import VictimHost
# Use the path_win32api_get_user_name fixture for all tests in this module # Use the path_win32api_get_user_name fixture for all tests in this module
@ -14,7 +16,6 @@ USER_LIST = ["user1", "user2"]
PASSWORD_LIST = ["pass1", "pass2"] PASSWORD_LIST = ["pass1", "pass2"]
LM_HASH_LIST = ["bogo_lm_1"] LM_HASH_LIST = ["bogo_lm_1"]
NT_HASH_LIST = ["bogo_nt_1", "bogo_nt_2"] NT_HASH_LIST = ["bogo_nt_1", "bogo_nt_2"]
DROPPER_TARGET_PATH_64 = "C:\\agent64"
mock_agent_repository = MagicMock() mock_agent_repository = MagicMock()
@ -24,7 +25,6 @@ mock_agent_repository.get_agent_binary.return_value = BytesIO(b"BINARY_EXECUTABL
@pytest.fixture @pytest.fixture
def powershell_arguments(http_and_https_both_enabled_host): def powershell_arguments(http_and_https_both_enabled_host):
options = { options = {
"dropper_target_path_win_64": DROPPER_TARGET_PATH_64,
"credentials": { "credentials": {
"exploit_user_list": USER_LIST, "exploit_user_list": USER_LIST,
"exploit_password_list": PASSWORD_LIST, "exploit_password_list": PASSWORD_LIST,
@ -114,7 +114,10 @@ def test_successful_copy(monkeypatch, powershell_exploiter, powershell_arguments
exploit_result = powershell_exploiter.exploit_host(**powershell_arguments) exploit_result = powershell_exploiter.exploit_host(**powershell_arguments)
assert DROPPER_TARGET_PATH_64 in str(mock_client.return_value.copy_file.call_args[0][1]) # Check if the copied agent name has randomness of 8 plus dash
assert len(str(DROPPER_TARGET_PATH_WIN64)) + RAND_SUFFIX_LEN + 1 == len(
str(mock_client.return_value.copy_file.call_args[0][1])
)
assert exploit_result.exploitation_success assert exploit_result.exploitation_success

View File

@ -3,38 +3,39 @@ from unittest.mock import Mock
import pytest import pytest
from infection_monkey.exploit.tools.helpers import RAND_SUFFIX_LEN, get_agent_dest_path from infection_monkey.exploit.tools.helpers import RAND_SUFFIX_LEN, get_agent_dest_path
from infection_monkey.model import DROPPER_TARGET_PATH_LINUX, DROPPER_TARGET_PATH_WIN64
def _get_host_and_options(os, path): def _get_host(os):
host = Mock() host = Mock()
host.os = {"type": os} host.os = {"type": os}
options = {"dropper_target_path_win_64": path, "dropper_target_path_linux": path} return host
return host, options
@pytest.mark.parametrize("os", ["windows", "linux"]) @pytest.mark.parametrize(
@pytest.mark.parametrize("path", ["C:\\monkey.exe", "/tmp/monkey-linux-64", "mon.key.exe"]) "os, path", [("linux", DROPPER_TARGET_PATH_LINUX), ("windows", DROPPER_TARGET_PATH_WIN64)]
)
def test_get_agent_dest_path(os, path): def test_get_agent_dest_path(os, path):
host, options = _get_host_and_options(os, path) host = _get_host(os)
rand_path = get_agent_dest_path(host, options) rand_path = get_agent_dest_path(host)
# Assert that filename got longer by RAND_SUFFIX_LEN and one dash # Assert that filename got longer by RAND_SUFFIX_LEN and one dash
assert len(str(rand_path)) == (len(str(path)) + RAND_SUFFIX_LEN + 1) assert len(str(rand_path)) == (len(str(path)) + RAND_SUFFIX_LEN + 1)
def test_get_agent_dest_path_randomness(): def test_get_agent_dest_path_randomness():
host, options = _get_host_and_options("windows", "monkey.exe") host = _get_host("windows")
path1 = get_agent_dest_path(host, options) path1 = get_agent_dest_path(host)
path2 = get_agent_dest_path(host, options) path2 = get_agent_dest_path(host)
assert path1 != path2 assert path1 != path2
def test_get_agent_dest_path_str_place(): def test_get_agent_dest_path_str_place():
host, options = _get_host_and_options("windows", "C:\\abc\\monkey.exe") host = _get_host("windows")
rand_path = get_agent_dest_path(host, options) rand_path = get_agent_dest_path(host)
assert str(rand_path).startswith("C:\\abc\\monkey-") assert str(rand_path).startswith(r"C:\Windows\temp\monkey")
assert str(rand_path).endswith(".exe") assert str(rand_path).endswith(".exe")